Reputation: 461
I wanted to try writing a template wrapper that checks whether a class has a member function. and for this it was necessary to use std::declval
template<typename T>
struct has_member<T, void_t<decltype(std::declval<T>().push_back())>>:std::true_type{};
As I saw, the implementation of declval should be like this:
template<typename T>
T&& declval() noexcept;
And actually, it may be a odd question ,but why does declval have no return statement ?
If I understand correctly, it should return rvalue to the place of its call:
template<typename T>
struct has_member<T, void_t<decltype(T&&.push_back())>>:std::true_type{};
But we don't use return in the implementation . Maybe it's because we don't have a function body?I would like to understand why this is possible. I would be happy to help
Upvotes: 3
Views: 2085
Reputation: 473407
declval
has no return
statement because the function has no implementation. If you ever tried to call declval
, you would get a compile error.
declval
exists to be used in what C++ calls an "unevaluated context". This is in a place where an expression will be parsed, the types used worked out, but the expression will never actually be evaluated. The expression given to decltype
is an unevaluated context.
Even though declval
has no implementation, it is a function with a well-defined return type. So even though you can't actually execute it, the compiler does know what the type of declval<T>()
will be. And therefore, the compiler can examine the expression containing it.
See, T&&
is a type; you cannot use .
on a type. The result of calling a function is an object (or void
), which has a type, but isn't itself a type. What you want to say is "assuming I have a value of type T
, I want to do X to it". You can't say that with T&&
because it's a type, not an object. And you don't want to limit T
to things which are default constructible, so you can't just say T{}
.
That's where declval
comes in.
Upvotes: 13
Reputation: 122458
From cppreference:
Note that declval can only be used in unevaluated contexts and is not required to be defined; it is an error to evaluate an expression that contains this function. Formally, the program is ill-formed if this function is odr-used.
The usage in your code can be simplified to
using type = decltype(std::declval<T>().push_back());
And decltype
:
Inspects the declared type of an entity or the type and value category of an expression.
std::declval
is never called, so it needs no definition. The declaration is enough for the compiler to deduce its return type.
In other words...
But we don't use return in the implementation
declval
has no implementation.
Upvotes: 4