std::move realisation - c++

I got next snippet from microsoft
template <typename T> struct RemoveReference {
typedef T type;
};
template <typename T> struct RemoveReference<T&> {
typedef T type;
};
template <typename T> struct RemoveReference<T&&> {
typedef T type;
};
template <typename T> typename RemoveReference<T>::type&& Move(T&& t) {
return t;
}
...
remote_integer x = frumple(5);
remote_integer&& x1 = Move(x);
and i get an error "error C2440: 'return' : cannot convert from 'remote_integer' to 'remote_integer &&'"
something changed in compilers? With std::move all goes right.

The reason your Move doesn't work, is because t is always lvalue (even when T&& resolves to, say, int&&). Even though it might seem weird, named rvalue references are indeed lvalues.
When returning from your Move, you attempt to implicitly bind lvalue to rvalue reference, which is forbidden by standard (§8.5.3). As noted in the comments, you have to cast t explicitly to rvalue reference.
Relevant parts of standard are §5/4 and §5/5, but I'm going to quote note §5/6, which sums this nicely:
In general, the effect of this rule is that named rvalue references
are treated as lvalues and unnamed rvalue references to objects are
treated as xvalues; rvalue references to functions are treated as
lvalues whether named or not.
Correct implementation is indeed:
template <typename T>
typename std::remove_reference<T>::type&& move(T&& t)
{
return static_cast<typename std::remove_reference<T>::type&&>(t);
}
As far as I remember, this code used to be valid in earlier drafts. But since the rules have changed, you have to provide explicit cast now (same applies to std::forward).

Related

rvalue reference or forwarding reference?

I know that for the following function
template <typename T>
void do_something(T&& arg);
the function parameter is a forwarding reference. But in the following case is it still a forwarding reference or an rvalue reference?
template <typename T>
class MyClass
{
void do_something(T&& arg);
};
I think it is still a forwarding reference, but I'm not sure. Furthermore, I'd like to know what can be done to enforce an rvalue reference or a forwarding reference, if the result is not what I intended.
It's an rvalue reference. Forwarding references can only appear in a deduced context. This is just a member function that accepts an rvalue reference to the class template parameter.
You can't force a forwarding reference to be an rvalue reference if you want to maintain template argument deduction for functions. If you don't mind specifying the template argument all over the place, then this will always and only ever give an rvalue reference:
template<typename T> struct identity { using type = T; };
template<typename T> void func(typename identity<T>::type&&);
In retrospect, there actually is a way to maintain deduction but force only rvalue refs to be accepted (besides the self documenting one in Simple's answer). You can provide a deleted lvalue overload:
template<typename T>
void func(T&) = delete;
template<typename T>
void func(T&& s)
{
// ...
}
The lvalue overload is more specialized when passed an lvalue. And on account of being deleted, will give a somewhat clear error message.
Furthermore I like to know, what can be done to enforce an rvalue reference
If you always want an rvalue reference in a deduced context (and not a forwarding reference), then you can use this:
template<
typename T,
typename = std::enable_if_t<!std::is_lvalue_reference<T>::value>
>
using rval_ref = T&&;
template<typename T>
void foo(rval_ref<T> s)
{
// ...
}
foo can only be called with an rvalue, and T will not be a reference (i.e. if you call foo with std::string&&, then T will be std::string).

Why does std::forward have two overloads?

Given the following reference collapsing rules
T& & --> T&
T&& & --> T&
T& && --> T&
T&& && --> T&&
The third and fourth rule imply that T(ref qualifer) && is the identity transformation, i.e. T& stays at T& and T&& stays at T&&. Why do we have two overloads for std::forward? Couldn't the following definition serve all purposes?
template <typename T, typename = std::enable_if_t<!std::is_const<T>::value>>
T&& forward(const typename std::remove_reference<T>::type& val) {
return static_cast<T&&>(const_cast<T&&>(val));
}
Here the only purpose the const std::remove_reference<T>& serves is to not make copies. And the enable_if helps ensure that the function is only called on non const values. I'm not entirely sure whether the const_cast is needed since it's not the reference itself that's const.
Since forward is always called with explicit template parameters there are two cases we need to consider:
forward<Type&>(val) Here the type of T in forward will be T& and therefore the return type will be the identity transformation to T&
forward<Type&&>(val) Here the type of T in forward will be T&& and therefore the return type will be the identity transformation to T&&
So then why do we need two overloads as described in http://en.cppreference.com/w/cpp/utility/forward?
Note: I am not sure if std::forward is ever used with const types, but I disabled forward in that case, because I have never seen it used like that. Also move semantics don't really make sense in that case either.
A good place to start would be Howard Hinnant's answer and paper on std::forward().
Your implementation handles all the normal use-cases correctly (T& --> T&, T const& --> T const&, and T&& --> T&&). What it fails to handle are common and easy-to-make errors, errors which would be very difficult to debug in your implementation but fail to compile with std::forward().
Given these definitions:
struct Object { };
template <typename T, typename = std::enable_if_t<!std::is_const<T>::value>>
T&& my_forward(const typename std::remove_reference<T>::type& val) {
return static_cast<T&&>(const_cast<T&&>(val));
}
template <class T>
void foo(T&& ) { }
I can pass non-const references to const objects, both of the lvalue variety:
const Object o{};
foo(my_forward<Object&>(o)); // ok?? calls foo<Object&>
foo(std::forward<Object&>(o)); // error
and the rvalue variety:
const Object o{};
foo(my_forward<Object>(o)); // ok?? calls foo<Object>
foo(std::forward<Object>(o)); // error
I can pass lvalue references to rvalues:
foo(my_forward<Object&>(Object{})); // ok?? calls foo<Object&>
foo(std::forward<Object&>(Object{})); // error
The first two cases lead to potentially modifying objects that were intended to be const (which could be UB if they were constructed const), the last case is passing a dangling lvalue reference.

A failure to instantiate function templates due to universal (forward) reference to a templated type

Universal references (i.e. "forward references", the c++ standard name) and perfect forwarding in c++11, c++14, and beyond have many important advantages; see here, and here.
In Scott Meyers' article referenced above (link), it is stated as a rule of thumb that:
If a variable or parameter is declared to have type T&& for some deduced type T, that variable or parameter is a universal reference.
Example 1
Indeed, using clang++ we see that the following code snippet will successfully compile with -std=c++14:
#include <utility>
template <typename T>
decltype(auto) f(T && t)
{
return std::forward<T>(t);
}
int x1 = 1;
int const x2 = 1;
int& x3 = x1;
int const& x4 = x2;
// all calls to `f` result in a successful
// binding of T&& to the required types
auto r1 = f (x1); // various lvalues okay, as expected
auto r2 = f (x2); // ...
auto r3 = f (x3);
auto r4 = f (x4);
auto r5 = f (int()); // rvalues okay, as expected
Given any description of universal references (forward references) and type deduction (see, for instance, this explanation) it is clear why the above works. Although, from the same explanation, it is not abundantly clear why the below fails to work as well.
(failed) Example 2
This question addresses the same issue. The provided answers do not, however, explain why templated types are not categorized as being "deduced".
What I am about to show (seemingly) satisfies the requirement stated above by Meyers. However, the following code snipped fails to compile, producing the error (among others for each call to f):
test.cpp:23:11: error: no matching function for call to 'f'
auto r1 = f (x1);
test.cpp:5:16: note: candidate function [with T = foo, A = int] not
viable: no known conversion from 'struct foo< int >' to 'foo< int > &&'
for 1st argument
decltype(auto) f (T< A > && t)
#include <utility>
//
// It **seems** that the templated type T<A> should
// behave the same as an bare type T with respect to
// universal references, but this is not the case.
//
template <template <typename> typename T, typename A>
decltype(auto) f (T<A> && t)
{
return std::forward<T<A>> (t);
}
template <typename A>
struct foo
{
A bar;
};
struct foo<int> x1 { .bar = 1 };
struct foo<int> const x2 { .bar = 1 };
struct foo<int> & x3 = x1;
struct foo<int> const& x4 = x2;
// all calls to `f` **fail** to compile due
// to **unsuccessful** binding of T&& to the required types
auto r1 = f (x1);
auto r2 = f (x2);
auto r3 = f (x3);
auto r4 = f (x4);
auto r5 = f (foo<int> {1}); // only rvalue works
In context, since the type T<A> of f's parameter is deduced, surely the parameter declaration T<A>&& t would behave as a universal reference (forward reference).
Example 3 (for clarity in describing the problem at hand)
Let me stress the following: the failure of the code in Example 2 to compile is not due to the fact that struct foo<> is a templated type. The failure seems to be cause only by the declaration of f's parameter as a templated type.
Consider the following revision to the previous code, which now does compile:
#include <utility>
//
// If we re-declare `f` as before, where `T` is no longer a
// templated type parameter, our code works once more.
//
template <typename T>
decltype(auto) f (T && t)
{
return std::forward<T> (t);
}
//
// Notice, `struct foo<>` is **still** a templated type.
//
template <typename A>
struct foo
{
A bar;
};
struct foo<int> x1 { .bar = 1 };
struct foo<int> const x2 { .bar = 1 };
struct foo<int> & x3 = x1;
struct foo<int> const& x4 = x2;
// all calls to `f` (again) result in
// a successful binding of T&& to the required types
auto r1 = f (x1);
auto r2 = f (x2);
auto r3 = f (x3);
auto r4 = f (x4);
It is astonishing to me that this simple change completely alters the behaviour of the type deduction for the template function f's type parameter.
Questions:
Why does the second example not work as expected? Are there techniques to overcome this problem with templated types in c++11/14? Are there well known, extant codebases (in the wild) making successful use of c++'s forward references with templated types?
When you call some function f with some lvalue:
int a = 42;
f(a);
Then f must be able to accept such an lvalue. This is the case when the first parameter of f is a (lvalue) reference type, or when it's not a reference at all:
auto f(int &);
auto f(int); // assuming a working copy constructor
This won't work when the parameter is a rvalue reference:
auto f(int &&); // error
Now, when you define a function with a forwarding reference as first parameter as you did in the first and third example ...
template<typename T>
auto f(T&&); // Showing only declaration
... and you actually call this function with an lvalue, template type deduction turns T into an (lvalue) reference (that this happens can be seen in the example code I provide in a moment):
auto f(int & &&); // Think of it like that
Surely, there are too much references involved above. So C++ has collapsing rules, which are actually quite simple:
T& & becomes T&
T& && becomes T&
T&& & becomes T&
T&& && becomes T&&
Thanks to the second rule, the "effective" type of the first parameter of f is a lvalue reference, so you can bind your lvalue to it.
Now when you define a function g like ...
template<template<class> class T, typename A>
auto g(T<A>&&);
Then no matter what, template parameter deduction must turn the T into a template, not a type. After all, you specified exactly that when declaring the template parameter as template<class> class instead of typename.
(This is an important difference, foo in your example is not a type, it's a template ... which you can see as type level function, but back to the topic)
Now, T is some kind of template. You cannot have a reference to a template.
A reference (type) is built from a (possibly incomplete) type. So no matter what, T<A> (which is a type, but not a template parameter which could be deduced) won't turn into an (lvalue) reference, which means T<A> && doesn't need any collapsing and stays what it is: An rvalue reference. And of course, you cannot bind an lvalue to an rvalue reference.
But if you pass it an rvalue, then even g will work.
All of the above can be seen in the following example:
template<typename X>
struct thing {
};
template<typename T>
decltype (auto) f(T&& t) {
if (std::is_same<typename std::remove_reference<T>::type, T>::value) {
cout << "not ";
}
cout << "a reference" << endl;
return std::forward<T>(t);
}
template<
template<class> class T,
typename A>
decltype (auto) g(T<A>&& t) {
return std::forward<T<A>>(t);
}
int main(int, char**) {
thing<int> it {};
f(thing<int> {}); // "not a reference"
f(it); // "a reference"
// T = thing<int> &
// T&& = thing<int>& && = thing<int>&
g(thing<int> {}); // works
//g(it);
// T = thing
// A = int
// T<A>&& = thing<int>&&
return 0;
}
(Live here)
Concerning how one could "overcome" this: You cannot. At least not the way you seem to want it to, because the natural solution is the third example you provide: Since you don't know the type passed (is it an lvalue reference, a rvalue reference or a reference at all?) you must keep it as generic as T. You could of course provide overloads, but that would somehow defeat the purpose of having perfect forwarding, I guess.
Hm, turns out you actually can overcome this, using some traits class:
template<typename> struct traits {};
template<
template<class>class T,
typename A>
struct traits<T<A>> {
using param = A;
template<typename X>
using templ = T<X>;
};
You can then extract both the template and the type the template was instantiated with inside of the function:
template<typename Y>
decltype (auto) g(Y&& t) {
// Needs some manual work, but well ...
using trait = traits<typename std::remove_reference<Y>::type>;
using A = typename trait::param;
using T = trait::template templ
// using it
T<A> copy{t};
A data;
return std::forward<Y>(t);
}
(Live here)
[...] can you explain why it is not an universal reference? what would the danger or the pitfall of it be, or is it too difficult to implement? I am sincerely interested.
T<A>&& isn't an universal reference because T<A> isn't a template parameter. It's (after deduction of both T and A) a simple (fixed / non generic) type.
A serious pitfall of making this a forwarding reference would be that you could no longer express the current meaning of T<A>&&: An rvalue reference to some type built from the template T with parameter A.
Why does the second example not work as expected?
You have two signatures:
template <typename T>
decltype(auto) f (T&& );
template <template <typename> typename T, typename A>
decltype(auto) f2 (T<A>&& );
f takes a forwarding reference, but f2 does not. The specific rule, from [temp.deduct.call] is, bold emphasis mine:
A forwarding reference is an rvalue
reference to a cv-unqualified template parameter. If P is a forwarding reference and the argument is an
lvalue, the type “lvalue reference to A” is used in place of A for type deduction.
With f, the argument is an rvalue reference to a template parameter (T). But with f2, T<A> is not a template parameter. Thus, that function simply takes as its argument an rvalue reference to T<A>. The calls fail to compile because all your arguments are lvalues and this case has no special exception for being called with an lvalue.
As for overcoming the problem, I guess a more or less equivalent way would be to deduce it with a forward reference, and trigger the comparison with T<A> manually.
template<typename T>
class X;
template<template<typename> class T, typename A>
class X<T<A>> {
public:
using type = A;
template<typename _>
using template_ = T<_>;
};
template<typename T, typename R>
struct copyref {
using type = T;
};
template<typename T, typename R>
struct copyref<T, R&> {
using type = T&;
};
template<typename T, typename R>
struct copyref<T, R&&> {
using type = T&&;
};
template <typename U, typename XX = X<std::decay_t<U>>,
typename = typename XX::type >
decltype(auto) f (U && t)
{
return std::forward<
typename copyref<
typename XX::template template_<typename XX::type>, U
>::type>(t);
}
If you don't actually want T<A> but a specific type, the best way is to use std::enable_if_t<std::is_same_v<std::decay_t<U>, SpecificType>>, which is way easier, I guess.
int main() {
static_assert(std::is_same<decltype(f(std::declval<X<int>&>())),
X<int>&>::value, "wrong");
static_assert(std::is_same<decltype(f(std::declval<X<int>>())),
X<int>&&>::value, "wrong");
// compile error
//f(std::declval<int>());
}
It is not enough to have type deduction. The form of the type declaration must be exactly T&& (an rvalue reference to just a template parameter). If it's not (or there is no type deduction) the parameter is an rvalue reference. If the argument is an lvalue, it won't compile. Since T<A>&& does not have that form, f (T<A> && t) is unable to accept an lvalue (as an lvalue reference) and you get the error. If you think that requires too much generality, consider that a simple const qualifier breaks it too:
template<typename T>
void f(const T&& param); // rvalue reference because of const
(putting aside the relative uselessness of a const rvalue reference)
The rules for reference collapsing simply do not kick in unless the most general form T&& is used. Without the ability for f to recognize an lvalue argument was passed and treat the parameter as an lvalue reference, there is no reference collapsing to be done (i.e. collapsing T& && to T& can't happen and it's just T<something>&&, an rvalue ref. to a templated type). The needed mechanism for the function to determine whether an rvalue or an lvalue is passed as an argument is encoded in the deduced template parameter. However, this encoding only occurs for a universal reference parameter, as strictly defined.
Why is this level of generality is necessary (besides just being the rule)? Without this specific definition format, universal references could not be super-greedy functions that instantiate to capture any type of argument... as they are designed to be. Daniel's answer gets to the point, I think: Suppose you want to define a function with a regular rvalue reference to a templated type parameter, T<A>&& (i.e. that does not accept an lvalue argument). If the following syntax were treated as a universal reference, then how would you change it to specify a regular rvalue reference?
template <template <typename> typename T, typename A>
decltype(auto) f (T<A> && t) // rv-ref - how else would you exclude lvalue arguments?
There needs to be a way to explicitly define the parameter as an rvalue reference to exclude lvalue arguments. This reasoning would seem to apply for other types of parameters, including cv qualifications.
Also, there seem to be ways around this (see traits and SFINAE), but I can't answer that part. :)

Understanding std::forward

Why the compiler is not able to deduce the template parameter for std::forward?
I mean:
#include <memory>
#include <iostream>
struct X{};
struct A{
A( const X& ) { std::cout << "cpy ctor\n"; }
A( X&& ) { std::cout << "move ctor\n"; }
};
X foo() { return {}; }
template<typename T,typename Arg>
T* factory( Arg&& a )
{
return new T(std::forward(a));
// ----------^^^^^^^^^^^^^^^ error: can't deduce template parameter
}
int main()
{
factory<A>(foo());
}
I know this is a design choice (due to the std::remove_reference in the definition of std::forward) to avoid the user forget to specify the type. What I can't get is: why the way it's implemented works to prevent deduction? Why the compiler is not just deducing forward's template parameter as Arg.
std::forward is declared like so:
template< class T >
T&& forward( typename std::remove_reference<T>::type& t );
typename std::remove_reference<T>::type is a non-deduced context. The compiler has no way of knowing which T should be deduced because it doesn't understand the semantic connection between the type member type and a given T. It would need to search through all types to find a match and be able to somehow disambiguate collisions. This is unreasonable, so the standard doesn't allow it.
The reason you have to specify a type for forward, by design, is what happens to a inside the function:
template<typename T,typename Arg>
T* factory( Arg&& a )
{
// 'a' is always an lvalue here
Since a is always an lvalue, there isn't enough information in a itself to be able to determine if it was passed in as an lvalue or rvalue. That information is only available via the type Arg, which will be either X or X&. Without that extra type information, it's impossible to know whether or now a must be forwarded as an lvalue or rvalue... which is why you need to provide it:
return new T(std::forward<Arg>(a));
}
From C++11 standard:
14.8.2.5 Deducing template arguments from a type
The non-deduced contexts are:
— The nested-name-specifier of a type that was specified using a qualified-id
— The expression of a decltype-specifier.
— A non-type template argument or an array bound in which a
subexpression references a template parameter.
— A template parameter used in the parameter type of a function
parameter that has a default argument that is being used in the call
for which argument deduction is being done.
etc...
std::forward is declared like this:
template<typename _Tp>
constexpr _Tp&& forward(typename std::remove_reference<_Tp>::type& __t) noexcept
According to first sentence above:
typename std::remove_reference<_Tp>::type is non deduced context.

When should I use remove_reference and add_reference?

I'm looking at [VC10's] unique_ptr and they do a couple things I don't understand:
typedef typename tr1::remove_reference<_Dx>::type _Dx_noref;
_Dx_noref& get_deleter()
{ // return reference to deleter
return (_Mydel);
}
unique_ptr(pointer _Ptr,
typename _If<tr1::is_reference<_Dx>::value, _Dx,
const typename tr1::remove_reference<_Dx>::type&>::_Type _Dt)
: _Mybase(_Ptr, _Dt)
{ // construct with pointer and (maybe const) deleter&
}
typename tr1::add_reference<_Ty>::type operator*() const
{ // return reference to object
return (*this->_Myptr);
}
Wouldn't just writing _Dx& or _Ty& be the same thing?
I actually do understand why they did it here though:
unique_ptr(pointer _Ptr, typename tr1::remove_reference<_Dx>::type&& _Dt)
: _Mybase(_Ptr, _STD move(_Dt))
{ // construct by moving deleter
}
get_deleter
Any reference is removed from the return type, then a reference is added back. In conformant C++11, adding a & to an existing & (or &&) produces a &. In C++03 however, that would be forming a reference to reference type, which was illegal. Likely MSVC is using the old rules, or that code was written when it did and remains because it is harmless.
constructor
Here they remove the reference, add const, and then add the reference back, to be passing by const reference. This is because adding const directly to a reference type does nothing! (§8.3.2/1) In either C++11 or C++03, the parameter declaration would be valid but would not add a const, if the reference weren't removed and replaced.
operator*
This is essentially the same as get_deleter, but they went about it a different way, and _Ty cannot be a reference type to begin with. It looks to me like _Ty& would suffice, but it's their prerogative.
Here's an example, possibly archetypal, for why we need remove_reference, in the implementation of std::move: The goal is to return an rvalue-reference type, based on the deduced type of the function argument.
Example: Foo x; move(x); Here move(x) should return a type Foo&&. But the argument of move is an expression of type Foo&. So how can the move function deduce the right type?
The first attempt is to use ordinary template argument deduction and use a cast:
template <typename T> T && move(??? x) { return static_cast<T&&>(x); }
But what should go into ???? If we say T x, then T will be deduced as Foo&; if we say T & x, then T = Foo, and if we say T && x, it won't match at all. The second version, T & x, appears to be useful.
But then the function doesn't work on rvalues to begin with (e.g. move(Foo(...)). In this case, we want T && x so that T = Foo and T&& = Foo&& as desired. We could have two overloads, but having multiple overloads is undesirable because it increases complexity needlessly. And finally, if someone were to specify the template paramete explicitly as move<Foo&>(x), the function would never work, because when T = Foo&, then T&& = Foo& as well.
So in comes remove_reference:
template <typename T>
typename std::remove_reference<T>::type && move(T && x)
{
return static_cast<typename std::remove_reference<T>::type &&>(x);
}
First off, the new reference collapsing rules imply that T is deduces as either Foo& or Foo&& in the two cases. Then, remove_reference strips the reference and gives type Foo in any case, and adding && makes the desired Foo&& return type.
In an oversimplified summary: we need remove_reference because (Foo&)&& is Foo& and not Foo&&. If you ever write template code that needs the base type of a template paramter that could be deduced as either U& or U&&, you can use this model.
template class add_reference has specialization for void, const void and const volatile void because reference of void type (void&) is not allowed. If _Ty& is used, it would generate a compilation error when _Ty = void.