As far as I know, I cannot declare an rvalue reference to void.
As an example, the following code is ill-formed:
void f(void &&v) { }
From [20.2.6/1] (function template declval) we have a declaration for declval that is:
template <class T>
add_rvalue_reference_t<T>
declval() noexcept;
Thus, declval<void> (let me say) would result in void &&, that I guessed it was ill-formed as well as in the previous example.
Anyway, the following minimal, working example compiles:
#include<utility>
int main() {
decltype(std::declval<void>())* ptr = nullptr;
}
Note that the following is true too:
static_assert(std::is_same<decltype(std::declval<void>()), void>::value, "!");
I would have expected it to be void&& as previously mentioned (or better, I was expecting it fails to compile).
Actually, it happens to be an rvalue reference for any other non-reference type.
As an example:
static_assert(std::is_same<decltype(std::declval<int>()), int&&>::value, "!");
Is declval<void> a valid expression or not? Is the code above legal?
Why does the behavior in case of void is different than with any other type? (For it wouldn't have worked otherwise could be an answer, if the code is legal).
If it's legal, where does the standard allow that? I've not been able to find the case.
Of course, the standard says:
The template parameter T of declval may be an incomplete type.
Anyway, here it would result in a non acceptable type (void&&) and it works around it discarding the rvalue reference.
add_rvalue_reference<T> only results in T&& if T is a referenceable type. So when T is void, the result is just void. This is also why you can add_rvalue_reference<int&&> and not get an error attempting to construct a reference to a reference. (Same with lvalue reference.)
Related
If I define a function which accepts an rvalue reference parameter:
template <typename T>
void fooT(T &&x) {}
I can call it, using GCC 4.5, with either a, ar, or arr:
int a, &ar = a, &&arr = 7;
fooT(a); fooT(ar); fooT(arr);
However, calling a similar, non-template function,
void fooInt(int &&x) {}
with any of those three arguments will fail. I was preparing to strengthen my knowledge of forward, but this has knocked me off course. Perhaps it's GCC 4.5; I was surprised to find that the first example from A Brief Introduction to Rvalue References also gives a compile error:
A a;
A&& a_ref2 = a; // an rvalue reference
The behavior of deduction in template parameters is unique, and is the reason your template version works. I've explained exactly how this deduction works here, in the context of another question.
Summarized: when the argument is an lvalue, T is deduced to T&, and T& && collapses to T&. And with the parameter at T&, it is perfectly valid to supply an lvalue T to it. Otherwise, T remains T, and the parameter is T&&, which accepts rvalues arguments.
Contrarily, int&& is always int&& (no template deduction rules to coerce it to something else), and can only bind to rvalues.
In addition to GMan's correct answer A Brief Introduction to Rvalue References has an incorrect example because it was written prior to a language change which outlawed:
A a;
A&& a_ref2 = a; // an rvalue reference (DISALLOWED in C++11)
Despite this change in the language, the main uses cases described in the article (move and forward) are still explained correctly in the article.
Update: Oh, and the same article was originally published here with (imho) slightly better formatting.
#include <iostream>
template <typename T>
class test
{
public:
test(T&& t)
{
}
};
template <typename T>
void succeed(T&& t)
{
}
int main()
{
int i = 1;
test<int> t(i); // failed to compile
succeed(i); // OK
return 0;
}
Error from GCC 5.2:
main.cpp: In function 'int main()':
main.cpp:20:18: error: cannot bind 'int' lvalue to 'int&&'
test t(i);
^
main.cpp:7:5: note: initializing argument 1 of 'test::test(T&&) [with T = int]'
test(T&& t)
^~~~
Could someone explain why the class template cannot compile but function template is OK?
Thanks.
In succeed, T&& t is a forwarding reference, not an rvalue reference. But in test, it is an rvalue reference.
A forwarding reference happens only when the parameter is T&&, and T is a template parameter of that function. In your code T is a template parameter of the enclosing class, so it doesn't count as a forwarding reference.
A forwarding reference may bind to both lvalues and rvalues.
During the drafting of C++11 it was suggested to use different syntax for forwarding references than rvalue references (instead of using T&& t for both); however the committee eventually settled on the current behaviour.
For a more detailed description of template parameter deduction, including a more precise specification of when T&& becomes a forwarding reference, see here -- search for the term "forwarding reference" to find the special rules for forwarding references.
Your confusion is probably rooted in your assumption that in both cases T is int. This is why you presume that these two cases as similar. In reality they are not.
In the class version you are manually specifying what T is. You explicitly tell the compiler that T is int. Constructor parameter type T && in this case becomes int &&, which cannot bind to a regular lvalue. Hence the error.
In the function version you don't tell the compiler what T is, but instead you expect the compiler to deduce it. In situations like yours the language is deliberately designed to deduce T as int & (note: not as int, but rather as int &). Once T is deduced as int &, the so called "reference collapsing" rules lead to function parameter type T && becoming int & - an ordinary lvalue reference. This parameter can successfully bind to lvalue argument i.
That explains the difference you observe.
For the sake of experiment, in the latter case you can suppress template argument deduction and specify the template argument explicitly
succeed<int>(i);
That will forcefully specify T as int and lead to the very same error as in the class version for the very same reason.
Similarly, you can "simulate" function's behavior for your class by specifying the template argument as int &
test<int &> t(i);
The same "reference collapsing" rules will make your constructor invocation to compile successfully.
Given the following declarations:
struct MyClass { };
typedef int MyClass::*Mp;
On both gcc 6.2 and Clang compiler I have tried, result_of<Mp(const MyClass)>::type yields int&&.
Summary of my question: Why int&& and not either const int&& or simply int?
More Background: The standard says that result_of is defined this way:
the member typedef type shall name the type
decltype(INVOKE(declval<Fn>(), declval<ArgTypes>()...));
The standard also defines INVOKE for pointer-to-member-objects this way:
— t1.*f when N == 1 and f is a pointer to data member of a class T and is_base_of_v<T, decay_t<decltype(t1)>> is true;
Note that the decay_t is only for testing whether this bullet applies. As far as I can tell, applying the two points above should yield:
decltype(declval<const MyClass>().*declval<Mp>())
Which yields const int&&. So, am I missing something, or are the compiler libraries wrong?
Edit, 30 Aug 2016:
Thanks for the responses. Several people have suggested alternative ways of getting the correct result without using result_of. I should clarify that the reason I am hung up on the correct definition of result_of is that I'm actually implementing the closest reasonable implementation of result_of that works with a pre-C++11 compiler. So, while I agree that I can use decltype or result_of<Mp(const MyClass&&)>::type in C++11, they do not do what I need for C++03. Several people have given the correct answer, which is that const rvalue arguments to functions are not part of the function type. This clarifies things for me and I will implement my pre-C++11 result_of such that it also discards those qualifiers.
const is stripped from function parameters. You can verify this using is_same.
void(int) == void(const int)
Mp(MyClass) == Mp(const MyClass)
result_of<Mp(MyClass)> == result_of<Mp(const MyClass)>
I think this is explained by [8.3.5.5]:
After producing the list of parameter types, any top-level
cv-qualifiers modifying a parameter type are deleted when forming the
function type. The resulting list of transformed parameter types and
the presence or absence of the ellipsis or a function parameter pack
is the function’s parameter-type-list. [ Note: This transformation
does not affect the types of the parameters. For example, int(*)(const
int p, decltype(p)*) and int(*)(int, const int*) are identical types.
— end note ]
You can work around it by defining your own result_of that does not (mis)use function types:
template <typename F, typename... ArgTypes>
struct my_result_of
{
using type = decltype(std::invoke(std::declval<F>(), std::declval<ArgTypes>()...));
};
This definition is really what the standard should have used.
In result_of_t<Mp(const MyClass)> you appear to be trying to ask what is the type of the result of invoking Mp with a const rvalue of type MyClass. A better way to ask that with result_of would be result_of_t<Mp(const MyClass&&)> but it's usually easier to just use decltype and forget that result_of ever existed. If you actually intended to ask the result with a const lvalue then that would be result_of_t<Mp(const MyClass&)>.
It is true that top-level const on function parameters has no meaning in a function declaration. When using result_of, therefore, it makes more sense to supply argument types as references to possibly-const types. This also makes the value category explicit, with no loss of expressivity. We can use the print_type trick to see what happens when we do this:
template <typename...> struct print_type; // forward declaration
print_type<std::result_of_t<Mp(const MyClass)>,
std::result_of_t<Mp(const MyClass&)>,
std::result_of_t<Mp(const MyClass&&)>,
std::result_of_t<Mp(MyClass)>,
std::result_of_t<Mp(MyClass&)>,
std::result_of_t<Mp(MyClass&&)>>{};
This prints:
error: invalid use of incomplete type 'struct print_type<int&&, const int&, const int&&, int&&, int&, int&&>'
So we can deduce:
std::result_of_t<Mp(const MyClass)> == int&&
std::result_of_t<Mp(const MyClass&)> == const int&
std::result_of_t<Mp(const MyClass&&)> == const int&&
std::result_of_t<Mp(MyClass)> == int&&
std::result_of_t<Mp(MyClass&)> == int&
std::result_of_t<Mp(MyClass&&)> == int&&
We can see that result_of_t<Mp(const MyClass)>, result_of_t<Mp(MyClass)>, and result_of_t<Mp(MyClass&&)> all mean the same thing. I would find it surprising if they didn't.
Note that when you use declval you are also providing argument types as references, as declval is declared to return a reference. Furthermore, all parameters to std::invoke are references.
The following code fails to compile when the template parameter T is a fundamental type such as int (on gcc 4.8). Is this standard conforming behaviour? My understanding of std::declval was that it always resolves to either a T&& or T&.
template <class T>
void foo(T&& val)
{
std::cout << noexcept(std::declval<typename std::decay<T>::type>() = val);
}
struct bar { };
bar b;
foo(b); // okay
int a;
foo(a); // error: using xvalue (rvalue reference) as lvalue
The error occurs at the point of assigning val to the std::declval expression.
It works if I remove the std::decay and use std::declval<T> directly, but I'm not sure why. The decayed type should just be int and so std::declval<int>() should have a return type of int&& shouldn't it?
You're trying to assign to an rvalue. std::declval<int>() correctly returns a type int&&. It has no name, so it's an rvalue (more precisely, xvalue) of type int. You're then trying to assign val into this rvalue, which is illegal for fundamental types.
Here's a live example demonstrating the problem in simplified form (without declval).
If I define a function which accepts an rvalue reference parameter:
template <typename T>
void fooT(T &&x) {}
I can call it, using GCC 4.5, with either a, ar, or arr:
int a, &ar = a, &&arr = 7;
fooT(a); fooT(ar); fooT(arr);
However, calling a similar, non-template function,
void fooInt(int &&x) {}
with any of those three arguments will fail. I was preparing to strengthen my knowledge of forward, but this has knocked me off course. Perhaps it's GCC 4.5; I was surprised to find that the first example from A Brief Introduction to Rvalue References also gives a compile error:
A a;
A&& a_ref2 = a; // an rvalue reference
The behavior of deduction in template parameters is unique, and is the reason your template version works. I've explained exactly how this deduction works here, in the context of another question.
Summarized: when the argument is an lvalue, T is deduced to T&, and T& && collapses to T&. And with the parameter at T&, it is perfectly valid to supply an lvalue T to it. Otherwise, T remains T, and the parameter is T&&, which accepts rvalues arguments.
Contrarily, int&& is always int&& (no template deduction rules to coerce it to something else), and can only bind to rvalues.
In addition to GMan's correct answer A Brief Introduction to Rvalue References has an incorrect example because it was written prior to a language change which outlawed:
A a;
A&& a_ref2 = a; // an rvalue reference (DISALLOWED in C++11)
Despite this change in the language, the main uses cases described in the article (move and forward) are still explained correctly in the article.
Update: Oh, and the same article was originally published here with (imho) slightly better formatting.