16

So I'm perplexed as to how this works. Given:

template <typename T>
int foo(T t) { t.foo(); }

It seems like this call should fail:

decltype(foo(int{ 13 })) fail = 42;

cout << fail << endl;

Instead it just prints:

42

It works this way on all the compilers I have access to. Is this correct behavior? I request a quote from the C++ Standard.

Siyual
  • 16,415
  • 8
  • 44
  • 58
Jonathan Mee
  • 37,899
  • 23
  • 129
  • 288

2 Answers2

17

In [dcl.spec] :

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

if e is an unparenthesized id-expression naming an lvalue or reference introduced from the identifier-list of a decomposition declaration, decltype(e) is the referenced type as given in the specification of the decomposition declaration ([dcl.decomp]);

otherwise, if e is an unparenthesized id-expression or an unparenthesized class member access ([expr.ref]), 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.

The operand of the decltype specifier is an unevaluated operand (Clause [expr]).

(Emphasis mine)

So your foo(int{ 13 }) is never evaluated.

Community
  • 1
  • 1
Hatted Rooster
  • 35,759
  • 6
  • 62
  • 122
  • Perfect answer, thanks. I was scared I was going to accept [Jesper Juhl's "because I said so" answer](http://stackoverflow.com/a/38373961/2642059). – Jonathan Mee Jul 14 '16 at 12:29
  • I wonder if this really implies that compilation must succeed. E.g. in `1 ? 0 : foo(int{13})` the call is never evaluated, but I wouldn't expect this to compile. (Probably there are some specific rules about what's exactly done in type-checking unevaluated operands... ?) – chi Jul 14 '16 at 12:42
  • 4
    If you change the definition of the above function template to `template auto foo(T t) { t.foo (); }`, instantiation of the template seems to happen though - which makes sense to me, since without return type deduction, there is no way for `decltype` to yield anything. This, of course, ends in a compile time error. Where in the above clause is this behavior reflected? Am I missing something? – thokra Jul 14 '16 at 13:13
  • @chi The term _unevaluated operand_ is a term of art defined by Clause [expr] of the standard, it doesn't just mean "an operand that is not evaluated". The 2nd/3rd operands of the `?:` operator are _potentially evaluated operands_ according to [basic.def.odr]. There is a non-normative note in [expr]: _"In an unevaluated operand... naming of objects or functions does not, by itself, require that a definition be provided."_ – Oktalist Jul 14 '16 at 13:35
  • 2
    @thokra [decl.spec.auto]: _"Return type deduction for a function template with a placeholder in its declared type occurs when the definition is instantiated even if the function body contains a return statement with a non-type-dependent operand. [Note: Therefore, any use of a specialization of the function template will cause an implicit instantiation.]"_ – Oktalist Jul 14 '16 at 13:46
5

Expressions in decltype are defined by the standard to not be evaluated, they are only parsed to get the type of the expression.

Jesper Juhl
  • 30,449
  • 3
  • 47
  • 70
  • 5
    Obligatory question to refer to the master document of the universe. – Hatted Rooster Jul 14 '16 at 12:17
  • 6
    That is one short answer. I'm going to make a comment on it that's longer than the answer itself: I have already established that the compilers I have access to support your statement. So the question is: "Is this correct behavior?" To establish that you'll need to reference something more than your acquired knowledge. – Jonathan Mee Jul 14 '16 at 12:19
  • @JonathanMee of what use is it to you? You are told that the compilers are correct, why do you need a spec reference? Are you going to verify his answer based on the spec text? How certain are you that you are not fooled by the answer into believing that the spec says it, regardless of what the spec actually says? – Johannes Schaub - litb Jul 14 '16 at 12:20
  • Yes. It's defined by the standard. I don't have the time right now to dig up the exact chapter and verse for you, but it *is* in there. I may have time to find the exact section for you later, but in the mean time you can go look yourself http://open-std.org/JTC1/SC22/WG21/docs/papers/2016/n4594.pdf – Jesper Juhl Jul 14 '16 at 12:24
  • 1
    @JohannesSchaub-litb I'm assuming you're asking: "Of what use would an official source be?" My answer to that question would be: I don't want to write code that depends on this if it's non-standard behavior that all the compilers I've tried just happened to implement... and may correct next generation. If it's standard behavior though, then I'll expect I can depend on it, and I'll exploit it in code. – Jonathan Mee Jul 14 '16 at 12:24
  • 3
    http://eel.is/c++draft/dcl.type.simple#4 _The operand of the decltype specifier is an unevaluated operand (Clause [expr])._ – Niall Jul 14 '16 at 12:25
  • 1
    @JonathanMee OK, that makes sense I think. So you are not after a spec text specifically, but would be satisfied by an official statetement, for example by the WG21 website? Note that even the spec does not necessarily determine what officially should be implemented. There are defects in the spec, and the last word is that of the committee. – Johannes Schaub - litb Jul 14 '16 at 12:27
  • @JohannesSchaub-litb Yeah, I think [Niall's comment](http://stackoverflow.com/questions/38373768/shouldnt-decltype-trigger-compilation-of-its-argument#comment64158668_38373961) and [Gill Bates answer](http://stackoverflow.com/a/38374157/2642059) have satisfied me that this *is* proper behavior. – Jonathan Mee Jul 14 '16 at 12:31
  • 2
    @GillBates you summed up this site perfectly – M.M Jul 14 '16 at 12:36