43

I am trying to understand how std::declval<T>() works. I know how to use it, and know what it does, mainly allows you to use decltype without constructing the object, like

decltype(std::declval<Foo>().some_func()) my_type; // no construction of Foo

I know from cppreference.com that std::declval<Foo> "adds" a rvalue reference to Foo, which due to reference collapsing rules ends up being either a rvalue reference or a lvalue reference. My question is why the constructor of Foo is not called? How can one implement a "toy" version of std::declval<T> without constructing the template parameter?

PS: I know it is not the same as the old trick

(*(T*)(nullptr))
Community
  • 1
  • 1
vsoftco
  • 55,410
  • 12
  • 139
  • 252
  • From the same page: "Note that because no definition exists for declval, it can only be used in unevaluated contexts; it is an error to evaluate an expression that contains this function." –  Feb 16 '15 at 00:07
  • 1
    @remyabel yes, I saw that, I just don't know how to "build" my own `declval` – vsoftco Feb 16 '15 at 00:08
  • 3
    `template< class T > typename std::add_rvalue_reference::type declval();` is literally all you need. [Live example](http://coliru.stacked-crooked.com/a/9a8d4e5cfa60355f) –  Feb 16 '15 at 00:10
  • 1
    Take a look at [Is there a reason declval returns add_rvalue_reference instead of add_lvalue_reference](https://stackoverflow.com/questions/20303250/is-there-a-reason-declval-returns-add-rvalue-reference-instead-of-add-lvalue-ref) and [Why does std::declval add a reference?](https://stackoverflow.com/questions/25707441/why-does-stddeclval-add-a-reference) –  Feb 16 '15 at 00:16
  • 1
    @remyabel thanks, I understand most of it, the only thing that escapes me is why can we use the `.` operator to select a function from a rvalue reference to an object without constructing the object, like `std::declval().f()` I know that `std::declval()` is of type `Foo&&`, but didn't know you can "access" it's member function via the `.` operator, as you didn't construct the object. I thought one should have use the scope operator `::` – vsoftco Feb 16 '15 at 00:22
  • 2
    @vsoftco: You're calling a non-static member function of an object. (well, you would be if the expression was evaluated) And for calling a non-static member function, you use a dot. Why would you expect that to be different in this case? Specifically, why would you expect the scope operator to be used? – Benjamin Lindley Feb 16 '15 at 00:43
  • @BenjaminLindley I think I find it a bit strange as you are not calling a function of a *constructed* object, but are just pretending to. – vsoftco Feb 16 '15 at 00:44
  • @vsoftco take a look at https://stackoverflow.com/questions/18749349/finding-offset-of-a-structure-element-in-c, i definitely read the answer `printf("%p\n", (void*)(&((struct s *)NULL)->i));` in some real code. and for me, `std::declval().f()` is a similar usage: extract meta information other than calling the function/access the variable – xgwang Jun 10 '20 at 06:11

1 Answers1

30

Basically, in a sizeof or decltype expression you can call functions that aren't implemented anywhere (they need to be declared, not implemented).

E.g.

class Silly { private: Silly( Silly const& ) = delete; };

auto foo() -> Silly&&;

auto main() -> int
{
    sizeof( foo() );
}

The linker should not complain about that.

Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
  • 1
    Ahh, ok, now it makes sense why "adding a rvalue reference" does the trick. For some reason I didn't know you can use `decltype` on just declared functions. The only thing I'm still a bit puzzled is why can you call `.f()` in `declval().f()`, I would have thought you need something like `declval()::f()` – vsoftco Feb 16 '15 at 00:11
  • 6
    @vsoft in an unevaluated context, all that matters are types. You don't need the instance to use the instance: you just need to pretend. What, after all, is the sound of one hand falling in the forest if there are two trolly tracks? – Yakk - Adam Nevraumont Feb 16 '15 at 00:42
  • I still don't get it. Wouldn't `auto foo() -> Silly;` work just as well? Or `Silly&`? Why r-value reference is needed here? – Violet Giraffe Dec 08 '18 at 18:08
  • @VioletGiraffe It's useful where you may not know the return type of `Foo::foo()` in advance and want to declare your type with the same type as the one returned by `Foo::foo()`. – vsoftco Mar 22 '19 at 13:05
  • 1
    @VioletGiraffe like above, when you do not know the type T at hand in template contexts but need to call a function of that type. You may not write T().foo(), because you can't make assumptions that T has default (or any other) constructor. Hence you need to call function through reference. I'm not sure exactly why rvalue reference is selected there. – Wormer Aug 13 '19 at 15:07
  • 1
    Does this really ansewr the question? – jw_ Jan 17 '20 at 09:52
  • 2
    @Wormer, I am a year late, but [this](https://stackoverflow.com/a/20303350/1116098) might answer why an rvalue reference is used in `declval()`. – Sean Francis N. Ballais May 30 '20 at 14:34