I'm writing a network library and use move semantics heavily to handle ownership for file descriptors. One of my class wishes to receive file descriptor wrappers of other kinds and take ownership, so it's something like
struct OwnershipReceiver
{
template <typename T>
void receive_ownership(T&& t)
{
// taking file descriptor of t, and clear t
}
};
It has to deal multiple unrelated types so receive_ownership has to be a template, and to be safe, I wish it ONLY binds to rvalue references, so that user has to explicitly state std::move when passing an lvalue.
receive_ownership(std::move(some_lvalue));
But the problem is: C++ template deduction allows an lvalue to be passed in without extra effort. And I actually shot myself on the foot once by accidentally passing an lvalue to receive_ownership and use that lvalue(cleared) later.
So here is the question: how to make a template ONLY bind to rvalue reference?
You can restrict T to not be an lvalue reference, and thus prevent lvalues from binding to it:
#include <type_traits>
struct OwnershipReceiver
{
template <typename T,
class = typename std::enable_if
<
!std::is_lvalue_reference<T>::value
>::type
>
void receive_ownership(T&& t)
{
// taking file descriptor of t, and clear t
}
};
It might also be a good idea to add some sort of restriction to T such that it only accepts file descriptor wrappers.
A simple way is to provide a deleted member which accepts an lvalue reference:
template<typename T> void receive_ownership(T&) = delete;
This will always be a better match for an lvalue argument.
If you have a function that takes several arguments, all of which need to be rvalues, we will need several deleted functions. In this situation, we may prefer to use SFINAE to hide the function from any lvalue arguments.
One way to do this could be with C++17 and the Concepts TS:
#include <type_traits>
template<typename T>
void receive_ownership(T&& t)
requires !std::is_lvalue_reference<T>::value
{
// taking file descriptor of t, and clear t
}
or
#include <type_traits>
void receive_ownership(auto&& t)
requires std::is_rvalue_reference<decltype(t)>::value
{
// taking file descriptor of t, and clear t
}
Going slightly further, you're able to define a new concept of your own, which may be useful if you want to reuse it, or just for extra clarity:
#include <type_traits>
template<typename T>
concept bool rvalue = std::is_rvalue_reference<T&&>::value;
void receive_ownership(rvalue&& t)
{
// taking file descriptor of t, and clear t
}
Note: with GCC 6.1, you'll need to pass -fconcepts to the compiler, as it's an extension to C++17 rather than a core part of it.
Just for completeness, here's my simple test:
#include <utility>
int main()
{
int a = 0;
receive_ownership(a); // error
receive_ownership(std::move(a)); // okay
const int b = 0;
receive_ownership(b); // error
receive_ownership(std::move(b)); // allowed - but unwise
}
I learnt something that seems to confuse people quite often: using SFINAE is OK, but I can't use:
std::is_rvalue_reference<T>::value
The only way it works as I want is
!std::is_lvalue_reference<T>::value
The reason is: I need my function to receive an rvalue, not an rvalue reference. A function conditionally enabled with std::is_rvalue_reference<T>::value will not receive an rvalue, but rather receives an rvalue reference.
For lvalue references, T is deduced to be an lvalue reference, and for rvalue references, T is deduced to be a non-reference.
So if the function binds to a rvalue reference, what is seen at the end by the compiler for a certain type T is:
std::is_rvalue_reference<T>::value
and not
std::is_rvalue_reference<T&&>::value
Unfortunately, it seems like trying out is_rvalue_reference<TF> (where TF is the perfectly-forwarded type) does not work well if you are actually trying to make overloads that distinguish between const T& and T&& (e.g. using enable_if in both, one with is_rvalue_reference_v<TF> and the other with !is_rvalue_reference_V<TF>).
A solution (albeit hacky) is to decay the forwarded T, then place the overloads in a container aware of these types. Generated this example:
Hup, I was wrong, just forgot to look at Toby's answer (is_rvalue_reference<TF&&>) -- though it's confusing that you can do std::forward<TF>(...), but I guess that's why decltype(arg) also works.
Anywho, here's what I used for debugging: (1) using struct overloads, (2) using the wrong check for is_rvalue_reference, and (3) the correct check:
/*
Output:
const T& (struct)
const T& (sfinae)
const T& (sfinae bad)
---
const T& (struct)
const T& (sfinae)
const T& (sfinae bad)
---
T&& (struct)
T&& (sfinae)
const T& (sfinae bad)
---
T&& (struct)
T&& (sfinae)
const T& (sfinae bad)
---
*/
#include <iostream>
#include <type_traits>
using namespace std;
struct Value {};
template <typename T>
struct greedy_struct {
static void run(const T&) {
cout << "const T& (struct)" << endl;
}
static void run(T&&) {
cout << "T&& (struct)" << endl;
}
};
// Per Toby's answer.
template <typename T>
void greedy_sfinae(const T&) {
cout << "const T& (sfinae)" << endl;
}
template <
typename T,
typename = std::enable_if_t<std::is_rvalue_reference<T&&>::value>>
void greedy_sfinae(T&&) {
cout << "T&& (sfinae)" << endl;
}
// Bad.
template <typename T>
void greedy_sfinae_bad(const T&) {
cout << "const T& (sfinae bad)" << endl;
}
template <
typename T,
typename = std::enable_if_t<std::is_rvalue_reference<T>::value>>
void greedy_sfinae_bad(T&&) {
cout << "T&& (sfinae bad)" << endl;
}
template <typename TF>
void greedy(TF&& value) {
using T = std::decay_t<TF>;
greedy_struct<T>::run(std::forward<TF>(value));
greedy_sfinae(std::forward<TF>(value));
greedy_sfinae_bad(std::forward<TF>(value));
cout << "---" << endl;
}
int main() {
Value x;
const Value y;
greedy(x);
greedy(y);
greedy(Value{});
greedy(std::move(x));
return 0;
}
With more modern C++, we can simply require that T&& is an rvalue reference:
#include <type_traits>
template<typename T> requires std::is_rvalue_reference_v<T&&>
void receive_ownership(T&&)
{
// taking file descriptor of t, and clear t
}
Simple demonstration:
#include <string>
#include <utility>
int main()
{
auto a = std::string{};
auto const b = a;
receive_ownership(a); // ERROR
receive_ownership(std::move(a)); // okay
receive_ownership(b); // ERROR
receive_ownership(std::move(b)); // okay - but unwise!
receive_ownership(std::string{}); // okay
}
Related
Suppose you have the following pair of functions:
void f(const int&) {
// Do something, making a copy of the argument.
}
void f(int&&) {
// Do the same thing, but moving the argument.
}
They are fairly redundant—the only difference between the functions being whether they copy or move their argument. Of course, we can do better by re-writing this as a single template function:
template<typename T>
void g(T&&) {
// Do something, possibly using std::forward to copy or move the argument.
}
This works, and is a commonly used idiom in practice. But the template might be instantiated into three functions, up from our two above. We can verify this occurs with the following piece of code:
#include <iostream>
template<typename T> constexpr char *type = nullptr;
template<> constexpr const char *type<int&> = "int&";
template<> constexpr const char *type<const int&> = "const int&";
template<> constexpr const char *type<int> = "int";
template<typename T>
void g(T&&) {
std::cout << reinterpret_cast<void*>(&g<T>)
<< " = &g<" << type<T> << ">" << std::endl;
}
int main() {
int i = 0;
const int& cr = 0;
g(i);
g(cr);
g(0);
return 0;
}
/*
Prints:
0x100f45080 = &g<int&>
0x100f45100 = &g<const int&>
0x100f45180 = &g<int>
*/
This has added a third function for the case when T = int&, which we didn't have when we were using our non-templated function f above. In this case, we don't actually need this non-const l-value reference version of the function—given f was sufficient for our original needs—and this increases the size of our code, especially if we have many template functions written this way that call each other.
Is there a way to write our function g above so that the compiler will automatically deduce T = const int& when g(i) is called in our example code? I.e., a way where we don't have to manually write g<const int&>(i) yet still get the desired behavior.
It is a subjective point-of-view to say "forward references" ("universal references") are better than dedicated overloads. There are certainly many cases where this is true, but if you want to have full control they won't do all the jobs.
You could explicitly make sure users do not pass non-const lvalue references, by adding
static_assert(!std::is_lvalue_reference<T>::value || std::is_const<typename std::remove_reference<T>::type>::value, "only call g with const argument");
inside g, but this is not in all cases a very good solution.
Or you do what is done for vector::push_back(...) and provide explicit overloads -- but this was your starting point, see https://en.cppreference.com/w/cpp/container/vector/push_back.
The 'correct' answer just depends on your requirements.
Edit:
the suggestion of #Sjoerd would look something like:
template <typename T>
class aBitComplicated {
public:
void func(T&& v) { internal_func(std::forward<T>(v)); }
void func(const T& v) { internal_func(v); }
private:
template <typename U>
void internal_func(U&& v) { /* your universal code*/ }
};
There also a bit more sophisticated/complicated version of this, but this here should be the most simple version to achieve what you asked for.
I was reading about rvalue references and perfect forwarding when I came across this article on MSDN: https://msdn.microsoft.com/en-us/library/dd293668.aspx
My question is about this example from the article:
#include <iostream>
#include <string>
using namespace std;
template<typename T> struct S;
// The following structures specialize S by
// lvalue reference (T&), const lvalue reference (const T&),
// rvalue reference (T&&), and const rvalue reference (const T&&).
// Each structure provides a print method that prints the type of
// the structure and its parameter.
template<typename T> struct S<T&> {
static void print(T& t)
{
cout << "print<T&>: " << t << endl;
}
};
template<typename T> struct S<const T&> {
static void print(const T& t)
{
cout << "print<const T&>: " << t << endl;
}
};
template<typename T> struct S<T&&> {
static void print(T&& t)
{
cout << "print<T&&>: " << t << endl;
}
};
template<typename T> struct S<const T&&> {
static void print(const T&& t)
{
cout << "print<const T&&>: " << t << endl;
}
};
// This function forwards its parameter to a specialized
// version of the S type.
template <typename T> void print_type_and_value(T&& t)
{
S<T&&>::print(std::forward<T>(t));
}
// This function returns the constant string "fourth".
const string fourth() { return string("fourth"); }
int main()
{
// The following call resolves to:
// print_type_and_value<string&>(string& && t)
// Which collapses to:
// print_type_and_value<string&>(string& t)
string s1("first");
print_type_and_value(s1);
// The following call resolves to:
// print_type_and_value<const string&>(const string& && t)
// Which collapses to:
// print_type_and_value<const string&>(const string& t)
const string s2("second");
print_type_and_value(s2);
// The following call resolves to:
// print_type_and_value<string&&>(string&& t)
print_type_and_value(string("third"));
// The following call resolves to:
// print_type_and_value<const string&&>(const string&& t)
print_type_and_value(fourth());
}
My question is, why does this call:
print_type_and_value(s1);
resolve to:
print_type_and_value<string&>(string& &&t)
If my understanding is correct, string& && is an rvalue reference to an lvalue reference. Why is this? The variable s1 is an lvalue (it is not temporary, it is addressable, and it can be accessed from multiple parts of the program), so shouldn't the call resolve to string& (a simple lvalue reference)? I don't see where the double reference came from. s1 is a value, not a reference, isn't it? Why does this call involve rvalues at all?
In more general terms, I am a bit confused as to when template parameters resolve to T& && (an rvalue reference to an lvalue reference?) or T&& & (an lvalue reference to an rvalue reference?).
So, could someone please explain the following:
Why did the call to print_type_and_value(s1) resolve to print_type_and_value(string& &&t) ?
In general, when does f(var) resolve to f(T& &&x) or f(T&& &x) ?
I've seen examples in which template parameters resolve to T&& &&, which looks to me like an rvalue reference to an rvalue reference. When does this happen?
Of course, I am aware of the reference collapsing rules, and I understand that T& & is collapsed to T&, but I'm wondering why the call in this example resolved to T& && in the first place.
Thanks in advance for your help!
Edit:
I understand the basics of reference collapsing, but one thing that I'd like to know is why this specific example behaved in the way it did.
Why did print_type_and_value(s1) resolve to print_type_and_value(string& &&t) and then collapse to print_type_and_value(string& t) ?
Edit 2:
Thanks a lot for your links! I'm starting to understand it.
I just have one more question. Why does the template type evaluate to string& when a variable of type string is passed?
Edit 3:
I've re-read the links you've posted, and I 100% get It now. Thanks again!
The reference collapsing rule make print_type_and_value<string&>(string& &&t) equivalent to print_type_and_value<string&>(string& t): there are no reference to reference.
Here is an excellent question/answer on SO regarding this rule.
I'm writing a network library and use move semantics heavily to handle ownership for file descriptors. One of my class wishes to receive file descriptor wrappers of other kinds and take ownership, so it's something like
struct OwnershipReceiver
{
template <typename T>
void receive_ownership(T&& t)
{
// taking file descriptor of t, and clear t
}
};
It has to deal multiple unrelated types so receive_ownership has to be a template, and to be safe, I wish it ONLY binds to rvalue references, so that user has to explicitly state std::move when passing an lvalue.
receive_ownership(std::move(some_lvalue));
But the problem is: C++ template deduction allows an lvalue to be passed in without extra effort. And I actually shot myself on the foot once by accidentally passing an lvalue to receive_ownership and use that lvalue(cleared) later.
So here is the question: how to make a template ONLY bind to rvalue reference?
You can restrict T to not be an lvalue reference, and thus prevent lvalues from binding to it:
#include <type_traits>
struct OwnershipReceiver
{
template <typename T,
class = typename std::enable_if
<
!std::is_lvalue_reference<T>::value
>::type
>
void receive_ownership(T&& t)
{
// taking file descriptor of t, and clear t
}
};
It might also be a good idea to add some sort of restriction to T such that it only accepts file descriptor wrappers.
A simple way is to provide a deleted member which accepts an lvalue reference:
template<typename T> void receive_ownership(T&) = delete;
This will always be a better match for an lvalue argument.
If you have a function that takes several arguments, all of which need to be rvalues, we will need several deleted functions. In this situation, we may prefer to use SFINAE to hide the function from any lvalue arguments.
One way to do this could be with C++17 and the Concepts TS:
#include <type_traits>
template<typename T>
void receive_ownership(T&& t)
requires !std::is_lvalue_reference<T>::value
{
// taking file descriptor of t, and clear t
}
or
#include <type_traits>
void receive_ownership(auto&& t)
requires std::is_rvalue_reference<decltype(t)>::value
{
// taking file descriptor of t, and clear t
}
Going slightly further, you're able to define a new concept of your own, which may be useful if you want to reuse it, or just for extra clarity:
#include <type_traits>
template<typename T>
concept bool rvalue = std::is_rvalue_reference<T&&>::value;
void receive_ownership(rvalue&& t)
{
// taking file descriptor of t, and clear t
}
Note: with GCC 6.1, you'll need to pass -fconcepts to the compiler, as it's an extension to C++17 rather than a core part of it.
Just for completeness, here's my simple test:
#include <utility>
int main()
{
int a = 0;
receive_ownership(a); // error
receive_ownership(std::move(a)); // okay
const int b = 0;
receive_ownership(b); // error
receive_ownership(std::move(b)); // allowed - but unwise
}
I learnt something that seems to confuse people quite often: using SFINAE is OK, but I can't use:
std::is_rvalue_reference<T>::value
The only way it works as I want is
!std::is_lvalue_reference<T>::value
The reason is: I need my function to receive an rvalue, not an rvalue reference. A function conditionally enabled with std::is_rvalue_reference<T>::value will not receive an rvalue, but rather receives an rvalue reference.
For lvalue references, T is deduced to be an lvalue reference, and for rvalue references, T is deduced to be a non-reference.
So if the function binds to a rvalue reference, what is seen at the end by the compiler for a certain type T is:
std::is_rvalue_reference<T>::value
and not
std::is_rvalue_reference<T&&>::value
Unfortunately, it seems like trying out is_rvalue_reference<TF> (where TF is the perfectly-forwarded type) does not work well if you are actually trying to make overloads that distinguish between const T& and T&& (e.g. using enable_if in both, one with is_rvalue_reference_v<TF> and the other with !is_rvalue_reference_V<TF>).
A solution (albeit hacky) is to decay the forwarded T, then place the overloads in a container aware of these types. Generated this example:
Hup, I was wrong, just forgot to look at Toby's answer (is_rvalue_reference<TF&&>) -- though it's confusing that you can do std::forward<TF>(...), but I guess that's why decltype(arg) also works.
Anywho, here's what I used for debugging: (1) using struct overloads, (2) using the wrong check for is_rvalue_reference, and (3) the correct check:
/*
Output:
const T& (struct)
const T& (sfinae)
const T& (sfinae bad)
---
const T& (struct)
const T& (sfinae)
const T& (sfinae bad)
---
T&& (struct)
T&& (sfinae)
const T& (sfinae bad)
---
T&& (struct)
T&& (sfinae)
const T& (sfinae bad)
---
*/
#include <iostream>
#include <type_traits>
using namespace std;
struct Value {};
template <typename T>
struct greedy_struct {
static void run(const T&) {
cout << "const T& (struct)" << endl;
}
static void run(T&&) {
cout << "T&& (struct)" << endl;
}
};
// Per Toby's answer.
template <typename T>
void greedy_sfinae(const T&) {
cout << "const T& (sfinae)" << endl;
}
template <
typename T,
typename = std::enable_if_t<std::is_rvalue_reference<T&&>::value>>
void greedy_sfinae(T&&) {
cout << "T&& (sfinae)" << endl;
}
// Bad.
template <typename T>
void greedy_sfinae_bad(const T&) {
cout << "const T& (sfinae bad)" << endl;
}
template <
typename T,
typename = std::enable_if_t<std::is_rvalue_reference<T>::value>>
void greedy_sfinae_bad(T&&) {
cout << "T&& (sfinae bad)" << endl;
}
template <typename TF>
void greedy(TF&& value) {
using T = std::decay_t<TF>;
greedy_struct<T>::run(std::forward<TF>(value));
greedy_sfinae(std::forward<TF>(value));
greedy_sfinae_bad(std::forward<TF>(value));
cout << "---" << endl;
}
int main() {
Value x;
const Value y;
greedy(x);
greedy(y);
greedy(Value{});
greedy(std::move(x));
return 0;
}
With more modern C++, we can simply require that T&& is an rvalue reference:
#include <type_traits>
template<typename T> requires std::is_rvalue_reference_v<T&&>
void receive_ownership(T&&)
{
// taking file descriptor of t, and clear t
}
Simple demonstration:
#include <string>
#include <utility>
int main()
{
auto a = std::string{};
auto const b = a;
receive_ownership(a); // ERROR
receive_ownership(std::move(a)); // okay
receive_ownership(b); // ERROR
receive_ownership(std::move(b)); // okay - but unwise!
receive_ownership(std::string{}); // okay
}
This is a question related to OP's solution to Is constexpr useful for overload.
Basically, he used
template<class T>
typename std::enable_if<std::is_arithmetic<T>::value, int>::type
f(T&& n) { ... }
and
template<class T>
typename std::enable_if<!std::is_arithmetic<T>::value, int>::type
f(T&& n) { ... }
to know whether f() has been called with is a compile-time variable (e.g. literal: f(42)) or an lvalue (e.g. local variable: f(argc)) as its argument.
Q: How does that work ? (I expected, in both calls, that the first overload would be called (i.e. std::is_arithmetic<T>::value == true)
Here is a full example:
Run It Online
#include <iostream>
#include <type_traits>
using std::cout;
using std::endl;
template<class T>
constexpr
typename std::enable_if<std::is_arithmetic<T>::value,
int>::type
inline f(T&& n)
{
//cout << "compile time" << endl;
return 1;
}
template<class T>
typename std::enable_if<!std::is_arithmetic<T>::value,
int>::type
inline f(T&& n)
{
//cout << "run time" << endl;
return 0;
}
int main(int argc, char* argv[])
{
const int rt = f(argc);
constexpr int ct = f(42);
cout << "rt: " << rt << endl;
cout << "ct: " << ct << endl;
}
A template function of the form
template <typename T>
void func(T&& t);
looks as if it takes an r-value reference. But in actual fact T&& here is what Scott Meyers calls a universal reference, otherwise known as a forwarding reference. Different things can happen depending on the value category of the argument. Let's have a look at each case:
t is a non-const lvalue, for example
int i = 0;
func(i);
In this case, T is deduced to be an lvalue reference to int, that is, T=int&.
t is a const lvalue, for example
const int i = 1;
func(i);
Similarly, in this case T is deduced to be const int&.
t is an rvalue, for example
func(1);
In this case, T is deduced to be int just as we might have expected
Exactly why these deductions happen this way is to do with the rules for reference collapsing; I highly recommend reading Scott Meyers' article on the subject if you're interested.
The last case above also illustrates the point that in C and C++, literals (except string literals) are always rvalues.
What does this have to do with the enable_if? Well if your f is called with an integer literal, then T is deduced to be plain int. Obviously, is_arithmetic<int> is true, so the second function gets SFINAE'd out and the first is called.
However, when called with an lvalue, T is deduced to be (const) int&. A reference is not arithmetic, so the first function disappears leaving only the second to be called.
I'm having trouble overloading a function to take a value either by const reference or, if it is an rvalue, an rvalue reference. The problem is that my non-const lvalues are binding to the rvalue version of the function. I'm doing this in VC2010.
#include <iostream>
#include <vector>
using namespace std;
template <class T>
void foo(const T& t)
{cout << "void foo(const T&)" << endl;}
template <class T>
void foo(T&& t)
{cout << "void foo(T&&)" << endl;}
int main()
{
vector<int> x;
foo(x); // void foo(T&&) ?????
foo(vector<int>()); // void foo(T&&)
}
The priority seems to be to deduce foo(x) as
foo< vector<int> & >(vector<int>& && t)
instead of
foo< vector<int> >(const vector<int>& t)
I tried replacing the rvalue-reference version with
void foo(typename remove_reference<T>::type&& t)
but this only had the effect of causing everything to resolve to the const-lvalue reference version.
How do I prevent this behaviour? And why is this the default anyway - it seems so dangerous given that rvalue-references are allowed to be modified, this leaves me with an unexpectedly modified local variable.
EDIT: Just added non-template versions of the functions, and they work as expected. Making the function a template changes the overload resolution rules? That is .. really frustrating!
void bar(const vector<int>& t)
{cout << "void bar(const vector<int>&)" << endl;}
void bar(vector<int>&& t)
{cout << "void bar(vector<int>&&)" << endl;}
bar(x); // void bar(const vector<int>&)
bar(vector<int>()); // void bar(vector<int>&&)
When you have a templated function like this you almost never want to overload. The T&& parameter is a catch anything parameter. And you can use it to get any behavior you want out of one overload.
#include <iostream>
#include <vector>
using namespace std;
template <class T>
void display()
{
typedef typename remove_reference<T>::type Tr;
typedef typename remove_cv<Tr>::type Trcv;
if (is_const<Tr>::value)
cout << "const ";
if (is_volatile<Tr>::value)
cout << "volatile ";
std::cout << typeid(Trcv).name();
if (is_lvalue_reference<T>::value)
std::cout << '&';
else if (is_rvalue_reference<T>::value)
std::cout << "&&";
std::cout << '\n';
}
template <class T>
void foo(T&& t)
{
display<T>();
}
int main()
{
vector<int> x;
vector<int> const cx;
foo(x); // vector<int>&
foo(vector<int>()); // vector<int>
foo(cx); // const vector<int>&
}
In order for T&& to bind to an lvalue reference, T must itself be an lvalue reference type. You can prohibit the template from being instantiated with a reference type T:
template <typename T>
typename std::enable_if<!std::is_reference<T>::value>::type foo(T&& t)
{
cout << "void foo(T&&)" << endl;
}
enable_if is found in <utility>; is_reference is found in <type_traits>.
The reason that the overload taking T&& is preferred over the overload taking a T const& is that T&& is an exact match (with T = vector<int>&) but T const& requires a qualification conversion (const-qualification must be added).
This only happens with templates. If you have a nontemplate function that takes a std::vector<int>&&, you will only be able to call that function with an rvalue argument. When you have a template that takes a T&&, you should not think of it as "an rvalue reference parameter;" it is a "universal reference parameter" (Scott Meyers used similar language, I believe). It can accept anything.
Allowing a T&& parameter of a function template to bind to any category of argument is what enables perfect forwarding.