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
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.
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.
Related
The 2nd edition of C++ Templates - The Complete Guide features the following footnote at page 436 (my bold):
Except that decltype(call-expression) does not require a nonreference, non-void return type to be complete, unlike call expressions in other contexts. Using decltype(std::declval<T>().begin(), 0) instead does add the requirement that the return type of the call is complete, because the returned value is no longer the result of the decltype operand.
The footnote refers to the fact that decltype(std::declval<T>().begin()) is used (ineffectively, based on the footnote) to test whether it is valid to call .begin() on a T. The code that uses it is the following (with some pieces of text around it for clarity:
the trick is to formulate the expression that checks whether we can call begin() inside a decltype expression for the default value of an additional function template parameter:
#include <utility> // for declval
#include <type_traits> // for true_type, false_type, and void_t
// primary template:
template<typename, typename = std::void_t<>>
struct HasBeginT : std::false_type {};
// partial specialization (may be SFINAE’d away):
template<typename T>
struct HasBeginT<T, std::void_t<decltype(std::declval<T>().begin())>>
: std::true_type {
};
Here, we use decltype(std::declval<T>().begin()) to test whether, given a value/object of type T (using std::declval to avoid any constructor being required), calling a member begin() is valid.
From this previous question of mine, I've understood that since operator, can be overloaded, the role of the , 0 is to trigger the otherwise absent overload resolution, which in turn needs the type of std::declval<T>().begin() to be complete.
However, the text from the book (see the part highlighted in bold above), doesn't mention operator,, nor overload resolution. Is that just bad wording? Or maybe it's just the same matter looked from a different perspective? Or what?
It seems the author forgot or disregarded the possibility of , being overloaded. The whole technique is defective in this regard, not just the wording.
So if begin() is valid and returns a complete type, but , is overloaded and can't be called for some reason, you'll get a false negative.
A more robust solution would be decltype(void(std::declval<T>().begin())).
Edit, in order to avoid confusion: decltype does not accept two arguments. See answers.
The following two structs can be used to check for the existance of a member function on a type T during compile-time:
// Non-templated helper struct:
struct _test_has_foo {
template<class T>
static auto test(T* p) -> decltype(p->foo(), std::true_type());
template<class>
static auto test(...) -> std::false_type;
};
// Templated actual struct:
template<class T>
struct has_foo : decltype(_test_has_foo::test<T>(0))
{};
I think the idea is to use SFINAE when checking for the existance of a member function, so in case p->foo() isn't valid, only the ellipses version of test, which returns the std::false_type is defined. Otherwise the first method is defined for T* and will return std::true_type. The actual "switch" happens in the second class, which inherits from the type returned by test. This seems clever and "lightweight" compared to different approaches with is_same and stuff like that.
The decltype with two arguments first looked surprising to me, as I thought it just gets the type of an expression. When I saw the code above, I thought it's something like "try to compile the expressions and always return the type of the second. Fail if the expressions fail to compile" (so hide this specialization; SFINAE).
But:
Then I thought I could use this method to write any "is valid expression" checker, as long as it depends on some type T. Example:
...
template<class T>
static auto test(T* p) -> decltype(bar(*p), std::true_type());
...
http://ideone.com/dJkLPF
This, so I thought, will return a std::true_type if and only if bar is defined accepting a T as the first parameter (or if T is convertible, etc...), i.e.: if bar(*p) would compile if it was written in some context where p is defined of type T*.
However, the modification above evaluates always to std::false_type. Why is this? I don't want to fix it with some complicated different code. I just want to know why it doesn't work as I expected it to. Clearly, decltype with two arguments works different than I thought. I couldn't find any documentation; it's only explained with one expression everywhere.
It's an comma-separated list of expressions, the type is identical to the type of the last expression in the list. It's usually used to verify that the first expression is valid (compilable, think SFINAE), the second is used to specify that decltype should return in case the first expression is valid.
decltype does not take two arguments. Simply, it can can have an expression as its argument, and the comma operator is one way of creating expressions. Per Paragraph 5.18/1:
[...] A pair of expressions separated by a comma is evaluated left-to-right; the left expression is a discarded-value
expression (Clause 5). Every value computation and side effect associated with the left expression
is sequenced before every value computation and side effect associated with the right expression. The type
and value of the result are the type and value of the right operand; the result is of the same value category
as its right operand, and is a bit-field if its right operand is a glvalue and a bit-field. If the value of the right
operand is a temporary (12.2), the result is that temporary.
Therefore:
static_assert(std::is_same<decltype(42, 3.14), double>::value, "Will not fire");
If a function template returns decltype(auto) (or another type specifier using auto) but the return statement would be ill-formed, does SFINAE result? Is the return statement considered to be the immediate context of the function signature?
Nothing in the N3690 draft seems to require this. By default, I guess SFINAE does not apply.
This seems unfortunate because you can write a function to forward to another function, but you cannot make its existence conditional on the delegate as when writing longhand. Furthermore, checking the existence of a peer nonstatic member function cannot be done without decltype(auto) because this cannot be used in a function signature. However this indicates a fundamental problem, as decltype(auto) provides a path to considering the class type as complete within a member signature, where it's not.
Has a proposal been written, or has the problem been formally analyzed anywhere?
The ability to treat the class type as complete within a member signature may have other implications… but that's just fodder for another question.
but the return statement would be ill-formed, does SFINAE result?
The proposal-n3638 says,
SFINAE
Since the return type is deduced by instantiating the template, if the instantiation is ill-formed, this causes an error rather than a substitution failure. This allows an auto function to return a lambda, which is not possible using the decltype(returned expression) pattern.
Hope that is what you're looking for.
Following up on Nawaz's link, the remaining questions are answered by N3690 §7.1.6.4/11:
If the type of an entity with an undeduced placeholder type is needed to determine the type of an expression, the program is ill-formed.
This means that even if SFINAE worked with return type deduction, it couldn't be used to query one function declaration from another. The signature is essentially invalid until the return statement is processed, which occurs at the closing brace of the class {} definition, and after the definitions of preceding members have been processed.
In a sense, all member decltype(auto) functions are incomplete with respect to preceding functions in the same class:
struct s {
void f() { a(); } // error: use of ‘auto s::a()’ before deduction of ‘auto’
auto a() { return 3; }
};
This is GCC's complaint; it goes away if the member declarations are reversed. This is because the function definitions are processed in order of declaration, when the } from the class definition is reached. If the statement a(); is processed before the return 3;, then the program is ill-formed.
Edit, in order to avoid confusion: decltype does not accept two arguments. See answers.
The following two structs can be used to check for the existance of a member function on a type T during compile-time:
// Non-templated helper struct:
struct _test_has_foo {
template<class T>
static auto test(T* p) -> decltype(p->foo(), std::true_type());
template<class>
static auto test(...) -> std::false_type;
};
// Templated actual struct:
template<class T>
struct has_foo : decltype(_test_has_foo::test<T>(0))
{};
I think the idea is to use SFINAE when checking for the existance of a member function, so in case p->foo() isn't valid, only the ellipses version of test, which returns the std::false_type is defined. Otherwise the first method is defined for T* and will return std::true_type. The actual "switch" happens in the second class, which inherits from the type returned by test. This seems clever and "lightweight" compared to different approaches with is_same and stuff like that.
The decltype with two arguments first looked surprising to me, as I thought it just gets the type of an expression. When I saw the code above, I thought it's something like "try to compile the expressions and always return the type of the second. Fail if the expressions fail to compile" (so hide this specialization; SFINAE).
But:
Then I thought I could use this method to write any "is valid expression" checker, as long as it depends on some type T. Example:
...
template<class T>
static auto test(T* p) -> decltype(bar(*p), std::true_type());
...
http://ideone.com/dJkLPF
This, so I thought, will return a std::true_type if and only if bar is defined accepting a T as the first parameter (or if T is convertible, etc...), i.e.: if bar(*p) would compile if it was written in some context where p is defined of type T*.
However, the modification above evaluates always to std::false_type. Why is this? I don't want to fix it with some complicated different code. I just want to know why it doesn't work as I expected it to. Clearly, decltype with two arguments works different than I thought. I couldn't find any documentation; it's only explained with one expression everywhere.
It's an comma-separated list of expressions, the type is identical to the type of the last expression in the list. It's usually used to verify that the first expression is valid (compilable, think SFINAE), the second is used to specify that decltype should return in case the first expression is valid.
decltype does not take two arguments. Simply, it can can have an expression as its argument, and the comma operator is one way of creating expressions. Per Paragraph 5.18/1:
[...] A pair of expressions separated by a comma is evaluated left-to-right; the left expression is a discarded-value
expression (Clause 5). Every value computation and side effect associated with the left expression
is sequenced before every value computation and side effect associated with the right expression. The type
and value of the result are the type and value of the right operand; the result is of the same value category
as its right operand, and is a bit-field if its right operand is a glvalue and a bit-field. If the value of the right
operand is a temporary (12.2), the result is that temporary.
Therefore:
static_assert(std::is_same<decltype(42, 3.14), double>::value, "Will not fire");
I have some questions concerning function templates.
My plan was to build a wrapper which derives from a user-defined class and
not only exports the public functions of that class but also its constructors.
So I decided I would use multiple constructor templates (which I presume work exactly
the same as function templates) with 1 to n parameters to satisfy most constructors needs.
These would than simply call the constructor and do something else afterwards, like
this:
template <class T>
class Wrapper : public T
{
public:
template <class U>
Wrapper(U &u) : T(u) { doSomething(); }
template <class U, class V>
Wrapper(U &u, V &v) : T(u,v) { doSomething(); }
...
};
My intent is to register the instance within the Wrapper-Ctor somewhere else and,
from that point on, it can receive calls to virtual functions defined in T.
I had to use the reference operator in the code above, in order to guarantee that
my Wrapper-Ctor does not have any side-effects on the parameters that were passed
(copy-construction).
To my surprise this always worked, except for temporaries, which is the reason why
I am confused about the types that are inferred by the compiler in this situation.
To simplify the situation I tried to do something similiar via a template function:
template <class T>
void foo(T &t)
{
int x = ""; // intentional error
}
Calling the function like this:
std::string a;
std::string &b = a;
foo(b);
To my surprise the compiler denotes [T = std::string] in its error message.
I would have expected for this to be [T = std::string&], which would have caused
passing a reference-to-reference, which is invalid.
So, why does the compiler deduce a value-type in this situation?
Is it even possible to create a Wrapper-Ctor that does what I want, does not
have any side-effects on the parameters and also accepts temporaries?
Thanks alot!
It looks like the C++ spec explicitly states that this is the intended behavior. Specifically, if you have a template function that takes in a parameter P that depends on a template type argument, if P is a reference, then the underlying type of the reference, rather than the reference type, is used to determine what type should be used for P (see §14.8.2.1/2). Moreover, this same section says that const and volatile qualifiers are ignored during this step, so the constness can be inferred automatically.
It is not possible in C++03 to provide such a thing without manually overloading for every combination of const and non-const parameters.
No expression ever has reference type. Therefor, when argument deduction deduces against the argument expression type, it cannot make a distinction between a and b because the arguments a and b both have the same type.
Refer to [expr]p5 in the spec
If an expression initially has the type "reference to T" (8.3.2, 8.5.3), the type is adjusted to T prior to any further analysis.
Somewhat late, but since I don't think this was answered completely...
For template parameter deduction, see the previous answers.
For your problem with temporaries, make the parameters const references (as in Wrapper(const U&)).
The thing is, temporaries are rvalues. The standard states that non-const references can only be bound to lvalues. Therefore, a standards compliant compiler won't let you pass temporaries(rvalues) as arguments to non-const reference parameters. (This doesn't have anything to do with templates in particular, it's a general rule).
This is to the best of my knowledge, so take it with a bit of scepticism.