Reduced sample code:
#include <iostream>
template<typename T>
void func(T &x)
{
std::cout << "non-const " << x << std::endl;
}
template<typename T>
void func(const T &x)
{
std::cout << "const " << x << std::endl;
}
template<typename ...ARGS>
void proxy(ARGS ...args)
{
func(args...);
}
int main()
{
int i = 3;
func(i);
func(5);
func("blah");
proxy(i);
proxy(5);
proxy("blah");
}
Expected output:
non-const 3
const 5
const blah
non-const 3
const 5
const blah
Actual output:
non-const 3
const 5
const blah
non-const 3
non-const 5
non-const blah
So somehow the const qualifier of the function parameter gets lost when put through the variadic template. Why? How can I prevent this?
PS: tested with GCC 4.5.1 and SUSE 11.4
You just stumble upon the forwarding problem. This issue is solved using perfect forwarding.
Basically, you need to take your parameters by rvalue-reference, and rely on std::forward to correctly forward them while keeping their nature:
template<typename ...Args>
void proxy(Args&& ...args)
{
func(std::forward<Args>(args)...);
}
As Luc already mentioned this is a problem of forwarding and the answer to how to prevent it is to use perfect forwarding. But I will try to address the other questions at the end:
So somehow the const qualifier of the function parameter gets lost when put through the variadic template. Why?
This has everything to do with type inference. Ignore that you are using variadic templates and consider the simplest one argument template:
template <typename T>
void one_arg_proxy( T arg ) {
func( arg );
}
At the place of call you have one_arg_proxy( 5 ), that is, the argument is an int rvalue. Type inference kicks in to figure out what the type T should be, and the rules dictate that T is int, so the call gets translated to one_arg_proxy<int>(5) and the instantiation of the template that gets compiled is:
template <>
void one_arg_proxy<int>( int arg ) {
func( arg );
}
Now the call to func takes an lvalue argument, and thus the version of func taking a non-const reference is a better match (no conversions required) than the one taking a const&, yielding the result that you are getting. The issue here is that func does not get called with the argument to proxy, but rather with the internal copy that proxy made of it.
Related
I've understood how std::move works and implemented my own version for practice only. Now I'm trying to understand how std::forward works:
I've implemented this so far:
#include <iostream>
template <typename T>
T&& forward_(T&& x)
{
return static_cast<T&&>(x);
}
/*template <typename T>
T&& forward_(T& x)
{
return static_cast<T&&>(x);
}*/
void incr(int& i)
{
++i;
}
void incr2(int x)
{
++x;
}
void incr3(int&& x)
{
++x;
}
template <typename T, typename F>
void call(T&& a, F func)
{
func(forward_<T>(a));
}
int main()
{
int i = 10;
std::cout << i << '\n';
call(i, incr);
std::cout << i << '\n';
call(i, incr2);
std::cout << i << '\n';
call(0, incr3); // Error: cannot bind rvalue reference of type int&& to lvalue of type int.
std::cout << "\ndone!\n";
}
Why must I provide the overloaded forward(T&) version taking an lvalue reference? As I understand it a forwarding reference can yield an lvalue or an rvalue depending on the type of its argument. So passing the prvalue literal 0 to call along with the incr3 function that takes an rvalue reference of type int&& normally doesn't need forward<T>(T&)?!
If I un-comment the forward_(T&) version it works fine!?
I'm still confused about: why if I only use the forward_(T&) version does it work for any value category? Then what is the point in having the one taking a forwarding reference forward_(T&&)?
If I un-comment the version taking lvalue reference to T& and the one taking forwarding reference T&& then the code works fine and I've added some messages inside both to check which one called. the result is the the one with T&& never called!
template <typename T>
T&& forward_(T& x)
{
std::cout << "forward_(T&)\n";
return static_cast<T&&>(x);
}
template <typename T>
T&& forward_(T&& x)
{
std::cout << "forward_(T&&)\n";
return static_cast<T&&>(x);
}
I mean running the same code in the driver program I've shown above.
A T&& reference stops being a forwarding reference if you manually specify T (instead of letting the compiler deduce it). If the T is not an lvalue reference, then T&& is an rvalue reference and won't accept lvalues.
For example, if you do forward_<int>(...), then the parameter is an rvalue reference and ... can only be an rvalue.
But if you do forward_(...), then the parameter is a forwarding reference and ... can have any value category. (Calling it like this makes no sense though, since forward_(x) will have the same value category as x itself.)
It is clear that you wander why having two versions of std::forward; one takes an l-value reference to the type parameter T& and the other takes a universal reference (forwarding) to the type parameter. T&&.
In your case you are using forward_ from inside the function template call which has forwarding reference too. The problem is that even that function call called with an rvalue it always uses forward_ for an lvalue because there's no way that call can pass its arguments without an object (parameter). Remember that a name of an object is an lvlaue even if it's initialized from an r-value. That is why always in your example forward_(T&) is called.
Now you ask why there's second version taking forwarding reference?
It is so simple and as you may have already guessed: it is used for r-values (the values not the names of those objects).
Here is an example:
template <typename T>
T&& forward_(T& x)
{
std::cout << "forward_(T&)\n";
return static_cast<T&&>(x);
}
template <typename T>
T&& forward_(T&& x)
{
std::cout << "forward_(T&&)\n";
return static_cast<T&&>(x);
}
int main()
{
int i = 10;
forward_(i); // forward(T&) (1)
forward_(5); // forward(T&&) (2)
forward_("Hi"); // forward(T&) (3)
}
Here i have small piece of code and it compiles and works just fine
(at least with my GCC 7.3.0 and Ubuntu 18.04):
#include <functional>
#include <string>
#include <iostream>
void func(int a, const std::string& b, const std::string& c)
{
std::cout << a << b << c << std::endl;
}
class Test
{
public:
template <typename ... ARGS>
bool func_to_bind(ARGS&& ... args) const {
func(args...);
return true;
}
template <typename ... ARGS>
void binding_func(ARGS&& ... args) const
{
auto func_obj = std::bind(&Test::func_to_bind<int&, ARGS&...>, this, 42, args...);
func_obj();
}
};
int main()
{
Test obj;
obj.binding_func(std::string("one"), std::string("two"));
}
The part that i don't understand is this line:
std::bind(&Test::func_to_bind<int&, ARGS&...>, this, 42, args...);
Why does compiler require to use references as template type parameters?
If i remove reference from int like this:
std::bind(&Test::func_to_bind<int, ARGS&...>, this, 42, args...);
It won't compile. Also if i change func_to_bind signature to this:
bool func_to_bind(ARGS& ... args) const
It will compile just fine even with missing reference.
Could anyone explain what's exactly going on here?
I also did some search and found this question:
How to combine std::bind(), variadic templates, and perfect forwarding?
But i don't completely understand the answer.
If you specify the template argument as int explicitly, then the parameter type of func_to_bind would become int&&, i.e. an rvalue-reference type. Note that the stored arguments are passed to the invokable object as lvalues by std::bind:
Otherwise, the ordinary stored argument arg is passed to the invokable object as lvalue argument:
The lvalue can't be bound to the rvalue-referece parameter then invocation fails.
If you specify the template argument as int& explicitly, then the parameter type of func_to_bind becomes int&, i.e. an lvalue-reference type; lvalue could be bound to lvalue-reference then it works fine.
And if you change the parameter type of func_to_bind to ARGS&, it'll be always an lvalue-reference, for the same reason above it'll work fine.
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'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
}
The following template definition
template <typename Func, typename ReturnType, typename... Arguments>
class Command
{
public:
Command(Func f) : m_func(f) { }
ReturnType operator()(Arguments... funcArgs) { return m_func(funcArgs...); }
private:
Func m_func;
};
gives an error message with gcc 4.7.3 (error: field 'Command::m_func' invalidly declared function type) when instantiated with the following test code:
void testFunction(int i, double d)
{
std::cout << "TestFunctor::operator()(" << i << ", " << d << ") called." << std::endl;
}
int main()
{
void (&fRef)(int, double) = TestFunction;
Command<void(int, double), void, int, double> testCommand(fRef);
}
The error message also occurs if I pass TestFunction without the address-of operator into the testCommand constructor, but disappears if I pass either an explicitly named function pointer or use the address-of operator to pass the parameter. I'm under the impression that this code should work given Chapter 5 of Modern C++ Design.
What is the reasoning behind not being able to store a reference to a function, but function pointers work fine? Are there any workarounds that would allow this to compile without losing support for being able to pass functors as arguments to Command's constructor as well?
Changing one line could fix it:
Command<void(*)(int, double), void, int, double> testCommand(fRef);
The difference is, you're passing a function pointer now, instead of a function type. (Functions aren't copyable, but pointers are).
The reference fRef decays to a function pointer when you pass it.
I wouldn't suggest using std::function if performance mattered.
See it live on Coliru
Note that with a little rewriting, you can make it all work much nicer:
int main()
{
auto command = make_command(testFunction);
command(1, 3.14);
}
To do this, I'd suggest changing the Command template to be:
template <typename Func>
class Command
{
Func m_func;
public:
Command(Func f) : m_func(f) { }
template <typename... A> auto operator()(A... args) const
-> decltype(m_func(args...))
{ return m_func(args...); }
};
And now you can have type-deduction on the Func template parameter by having a factory function:
template <typename Func> Command<Func> make_command(Func f)
{
return Command<Func>(f);
}
See this approach live on Coliru too. Of course, the output it the same:
TestFunctor::operator()(1, 3.14) called.
C++11 offers an std::function template. You don't have to mess with function pointers.
You can pass those by reference, copy them, move them and they can even be used to store lambdas:
std::function<void()> func = []() { std::cout << "Hi" << std::endl; };