5

The following is a contrived example of the actual code:

int** Ptr = 0;
decltype(Ptr[0]) Test = (int*)0;

I get the error:

error C2440: 'initializing': cannot convert from 'int *' to 'int *&'

I'm not sure why I'm getting that since from my understand of decltype (correct me if I'm wrong) it just takes whatever expression you give it and resolve it to its actual type. In this case Ptr[0] is an int* so I'm expecting: int* Test = (int*)0;

What am I missing? Why am I getting that error?

Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
vexe
  • 5,433
  • 12
  • 52
  • 81
  • Maybe the type of `Ptr[0]` is an lvalue reference, so trying to initialize it with a prvalue fails. – Weak to Enuma Elish Nov 09 '15 at 01:06
  • Duplicate of this http://stackoverflow.com/questions/17241614/in-c-what-expressions-yield-a-reference-type-when-decltype-is-applied-to-them `Ptr[0]` converted to `*(Ptr + 0)`, and what `decltype` of `*Ptr` explained in the link above – fghj Nov 09 '15 at 01:12

2 Answers2

7

If we go to the draft C++ standard section 7.1.6.2 Simple type specifiers [dcl.type.simple] and see what the cases are, for decltype it starts out saying:

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

We see that in this case the expression is not an id-expression nor a class member access, which would give the result you expected(emphasis mine):

  • if e is an unparenthesized id-expression or an unparenthesized class member access (5.2.5), 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;

but the result is an lvalue:

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

which results in a reference.

As M.M point out std::remove_reference could be used to obtain the result you want:

std::remove_reference<decltype(Ptr[0])>::type Test = (int*)0;

As T.C. points out std::decay is also an option and is shorter:

std::decay<decltype(Ptr[0])>::type Test = (int*)0;
pro-gramer
  • 166
  • 1
  • 3
  • 14
Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
  • Thanks for the answer. Not sure of their reasoning behind implementing it this way, doesn't make sense to me. Is there no way (cast or whatever C++ shnanigan) to have `decltype(Ptr[0])` yield a `int*`? (remove the `&`) – vexe Nov 09 '15 at 01:16
  • 1
    @vexe you can use `std::remove_reference` – M.M Nov 09 '15 at 03:00
  • `auto` is even shorter :) – Barry Nov 09 '15 at 15:32
  • decay gets rid of const qualifiers whilst remove_reference does not – CashCow Feb 07 '19 at 12:49
3

In addition to all the other answers, you may also just use

int ** ptr = 0;
decltype(+ptr[0]) test = (int*)0;
// (+*p) is now an r-value expression of type int, rather than int&

Some of the rules used here are:

  • if the value category of expression is lvalue, then decltype yields T&;
  • if the value category of expression is prvalue, then decltype yields T.

Also note that if the name of an object is parenthesized, it is treated as an ordinary lvalue expression, thus decltype(x) and decltype((x)) are often different types. [an excerpt from https://en.cppreference.com/w/cpp/language/decltype]

seen at http://www.cplusplus.com/forum/beginner/149331/
more on decltype here : https://en.cppreference.com/w/cpp/language/decltype

Alex Vask
  • 119
  • 8
  • Your answer would be **much** better if you added an explanation as to how the unary `+` operator applied in `+ptr[0]` resolves the issue. – Adrian Mole Mar 05 '20 at 14:05
  • 1
    @AdrianMole Yeah, sure, updated. Thanks. Hopefully the explanation is ok now. – Alex Vask Mar 05 '20 at 14:15