How does std::declval<T>() work?
Asked Answered
C

1

46

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))
Callaghan answered 16/2, 2015 at 0:5 Comment(8)
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."Sickly
@remyabel yes, I saw that, I just don't know how to "build" my own declvalCallaghan
template< class T > typename std::add_rvalue_reference<T>::type declval(); is literally all you need. Live exampleSickly
Take a look at Is there a reason declval returns add_rvalue_reference instead of add_lvalue_reference and Why does std::declval add a reference?Sickly
@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<Foo>().f() I know that std::declval<Foo>() 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 ::Callaghan
@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?Bloodworth
@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.Callaghan
@Callaghan take a look at #18749849, i definitely read the answer printf("%p\n", (void*)(&((struct s *)NULL)->i)); in some real code. and for me, std::declval<Foo>().f() is a similar usage: extract meta information other than calling the function/access the variableWillenewillet
M
33

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.

Mispleading answered 16/2, 2015 at 0:10 Comment(7)
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<Foo>().f(), I would have thought you need something like declval<Foo>()::f()Callaghan
@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?Kitten
I still don't get it. Wouldn't auto foo() -> Silly; work just as well? Or Silly&? Why r-value reference is needed here?Lowney
@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().Callaghan
@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.Tanta
Does this really ansewr the question?Ranjiv
@Wormer, I am a year late, but this might answer why an rvalue reference is used in declval().Footless

© 2022 - 2024 — McMap. All rights reserved.