Remove Reference in Decltype (Return T Instead of T& Where T& Is the Decltype)

Remove reference in decltype (return T instead of T& where T& is the decltype)

To remove a reference:

#include <type_traits>

static_assert(std::is_same<int, std::remove_reference<int&>::type>::value, "wat");

In your case:

template <typename T>
auto doSomething(const T& foo)
-> typename std::remove_reference<decltype(foo.bar())>::type
{
return foo.bar();
}

Just to be clear, note that as written returning a reference is just fine:

#include <type_traits>

struct f
{
int& bar() const
{
static int i = 0;
return i;
}
};

template <typename T>
auto doSomething(const T& foo)
-> decltype(foo.bar())
{
return foo.bar();
}

int main()
{
f x;
return doSomething(x);
}

The returned reference can simply be passed on without error. Your example in the comment is where it becomes important and useful:

template <typename T>
auto doSomething(const T& foo)
-> decltype(foo.bar())
{
return foo.bar() + 1; // oops
}

C++: Why decltype (*this) returns a reference?

decltype deduces the type of expression, unless it is applied to a variable, in which case it deduces the type of that variable:

The type denoted by decltype(e) is defined as follows:

— if e is an unparenthesized id-expression or an unparenthesized class member access, decltype(e) is the type of the entity named by e. If there is no such entity, or if e names a set of overloaded functions, the program is ill-formed;

— otherwise, if e is an xvalue, decltype(e) is T&&, where T is the type of e;

— otherwise, if e is an lvalue, decltype(e) is T&, where T is the type of e;

— otherwise, decltype(e) is the type of e.

§7.1.6.2 [dcl.type.simple]

Dereferencing a pointer yields an lvalue, and therefore decltype will deduce an lvalue reference to the type of the pointee:

The unary * operator performs indirection: the expression to which it is applied shall be a pointer to an object type, or a pointer to a function type and the result is an lvalue referring to the object or function to which the expression points.

§5.3.1 [expr.unary.op]

Therefore decltype(*p), for some pointer p, deduces an lvalue reference to the type of the pointee.

If you wish to get the type of the pointee from some pointer p, you can use:

std::remove_pointer<decltype(p)>::type

Or:

std::remove_reference<decltype(*p)>::type

Or, in your example, you can simply say foo, type deduction is not required.

How to remove decltype(&MyClass::funct) part by extending the following type traits?

You can provide a function which will create required object. This is very simple to achieve:

template<typename T, typename ...Args>
auto makeObjectForMethod(T&&, Args&& ...args) -> get_class_t<decltype(&MyClass::funct)>
{
using R = get_class_t<decltype(&MyClass::funct)>;
return R{ std::forward(args)... };
}

int main()
{
auto myObj = makeObjectForMethod(&MyClass::funct);

myObj.funct();
return 0;
}

Works with C++11 and is quite handy:
https://wandbox.org/permlink/usMa3fA0I2HCNJ7M

The only disadvantage that in case of class fields it is not very helpful.

How can I use decltype to get a pointer without a reference?

*array is an lvalue expression, then decltype yields T&, i.e. MyClass*&.

b) if the value category of expression is lvalue, then decltype yields T&;

You can perform std::remove_reference (or std::decay depending on your intent) on the type, e.g.

std::vector<std::remove_reference_t<decltype(*array)>> ret(indices->size());

BTW: decltype(&(**array)) should work, you're giving one more *.

c++ why decltype(*pointer) yields a reference?

Contrary to apparently popular belief, *p has type int. From [expr.unary.op]

The unary * operator performs indirection: the expression to which it is applied shall be a pointer to an object type, or a pointer to a function type and the result is an lvalue referring to the object or function to which the expression points. If the type of the expression is “pointer to T”, the type of the result is “T”.

The reason decltype(*p) yields int& is because of how decltype works. From [dcl.type.simple]

For an expression e, the type denoted by decltype(e) is defined as follows: [...]

  • otherwise, if e is an unparenthesized id-expression or an unparenthesized class member access, decltype(e) is the type of the entity named by e. If there is no such entity, or if e names a set of overloaded functions, the program is ill-formed;

  • otherwise, if e is an xvalue, decltype(e) is T&&, where T is the type of e;

  • otherwise, if e is an lvalue, decltype(e) is T&, where T is the type of e; [...]

Here id-expression means an expression exactly consisting of a possibly parenthesized name.

Since *p is an lvalue expression of type int and not an unparenthesized id-expression nor class member access, the third bullet applies and decltype(*p) is int&.

It is worthy to note i is an unparenthesized id-expression, therefore the first bullet applies and decltype(i) is int.

How to deduce the return type of a function which takes a reference as parameter

The first fails because the class is not complete in the function declaration, as it is in member function bodies, so you can only use members that have already been declared.

For the second, the standard library provides declval, a function template declared to return its template parameter type. You can use this in unevaluated contexts when you need an expression of a particular type.

So the following version should work:

#include <utility> // for declval

template<typename data_type, typename functor_type>
class MyClass
{
private:
// Declare this before `func1`
data_type m_data;

public:
// Use the already declared member variable
auto func1() -> decltype(functor_type::process(m_data))
{
return functor_type::process(m_data);
}

// Or use `declval` to get an expression with the required reference type
auto func2() -> decltype(functor_type::process(std::declval<data_type&>()))
{
return functor_type::process(m_data);
}
};

Why does decltype on value returned from template function return T

decltype() gives you just that: a type. Not a string. You can't stream a type to the console like you are trying to.

If you want to get a string representation of some type, you can do that, though not in a portable fashion.

How to handle void decltype();

Unfortunately, I think you're stuck here with having to SFINAE on the return type. Start with a type trait (this is way cleaner than your decltype expression)

template <typename MF, typename... Args>
using Res = typename std::result_of<MF(C, Args...)>::type;

And then just switch:

template <typename MF, typename... Args>
typename std::enable_if<
std::is_same<Res<MF, Args...>, void>::value
>::type safeOperation(string key, MF mfp, Args... args)
{
/* void case */
}

template <typename MF, typename... Args>
typename std::enable_if<
!std::is_same<Res<MF, Args...>, void>::value,
Res<MF, Args...>
>::type safeOperation(string key, MF mfp, Args... args)
{
/* non-void case */
}

Or you could tag dispatch on is_void:

template <typename MF, typename... Args>
Res<MF, Args...> safeOperation(string key, MF mfp, Args... args)
{
return safeOperation(std::is_void<Res<MF, Args...>>{},
key, mfp, args...);
}

with:

template <typename MF, typename... Args>
void safeOperation(std::true_type /* void */,
string key, MF mfp, Args... args)
{ .. }

template <typename MF, typename... Args>
Res<MF, Args...> safeOperation(std::false_type /* non-void */,
string key, MF mfp, Args... args)
{ .. }

c++ 11 Trailing return type with decltype does not work as expected

It doesn't matter what the condition with the ?: operator is. The result type is calculated as a common type of the second and third operand. Here is part of how the common type and the value category of the ?: operator are calculated, see cppreference.com for the full details:

If the second and third operand are lvalues of the same type, then the result type will be a lvalue of that type.

If the types are unrelated lvalues, there are some more complex rules to determine the common type, but the result will be a prvalue, not a lvalue. In particular if the two types are arithmetic types such as double and long, then the usual arithmetic conversions are applied to obtain a common type. In the case of long and double that common type would be double. This is the same type calculation that would be done if you e.g. tried to add two different arithmetic type with +, hence the name usual arithmetic conversions.

Therefore decltype(a<b ? a:b) will be a reference type if a and b have the same type and otherwise it will not be a reference type.

This is why the function compiles. The common type is always such that both input types can be converted to it. It is also why the function has undefined behavior if the types are equal, because then decltype gives a reference and so you are going to return a reference to one of the function parameters.

decltype(a<b ? a:a) doesn't work with different types, because the common type of a and a is, as described above, a reference of the type of a. If b then has a different unrelated type, the result of a > b ? a : b will be a prvalue which cannot be bound to a lvalue reference.



Related Topics



Leave a reply



Submit