1

On the cppreference-page for std::declval it says the following:

Return value

Cannot be called and thus never returns a value.

What does this mean? Surely we call it when we use it?

struct Foo { int func(){}  };

decltype(std::declval<Foo>().func()) integer;
/*                        ^
                          |                      */ 
François Andrieux
  • 28,148
  • 6
  • 56
  • 87
JensB
  • 839
  • 4
  • 19
  • 3
    `decltype(std::declval().func())` never calls `declval`, it only looks at the return value. – tkausl Dec 14 '21 at 19:37
  • @DrewDormann Are you sure? I think @tkausl is more correct, that `std::declval` cannot be called and `decltype` just looks at the type of the return value – JensB Dec 14 '21 at 19:39
  • @JensB You may not *evaluate* `std::declval` which basically means execution is never allowed try to run the function. Normally a compiler will produce an error if you try. – François Andrieux Dec 14 '21 at 19:41
  • I think what's key to any answer here is that any expression inside `decltype()` is _inspected_, but not _evaluated_. The "call" indicated in this code sample _looks like a call_, but actually is never called. – Drew Dormann Dec 14 '21 at 19:44

1 Answers1

9

The program is ill-formed if std::declval<T> is odr-used. However, cppreference tries to phrase this in a way that most users will find easier to understand.

A call to a function, or taking the address of the function, will normally odr-use that function. However, this is only the case if the call/address is actually evaluated. Thus, it is legal to call std::declval<T> as long as the call isn't evaluated. (Note that it's not enough merely to prove that the call will never occur at runtime. Rather, the call must occur in certain types of contexts in which the rules of the language prevent it from being evaluated. Therefore, the compiler can always check this condition.)

decltype does not evaluate its operand, so it is usually legal to place a call to std::declval<T> inside decltype. However, there are some situations where parts of the operand are evaluated anyway. For example, decltype(std::array<int, std::declval<int>()>) won't compile, because it will try to evaluate std::declval<int>() in order to get the size of the array.

Brian Bi
  • 111,498
  • 10
  • 176
  • 312