I'm trying to use an overloaded function to hide passing an int reference to a utility function when I don't care for the returned int data. Why is it that I need to use T&
instead of T
as the template argument? And why do I need the intermediate function that defines i
as an rvalue?
class DataClass
{
};
template <typename T>
bool func(T& data, int& i) {
i = 1;
cout << "it worked!" << endl;
}
// I would like to remove this function
template <typename T>
bool func(T& data, int&& i) {
return func(std::forward<T&>(data), std::forward<int&>(i));
}
template <typename T>
bool func(T& data) {
// Why doesn't this line work?
// return func(std::forward<T>(data), std::forward<int>(0));
return func(std::forward<T&>(data), 0);
}
int main(int argc, char ** argv)
{
DataClass d;
func<DataClass>(d);
return 0;
}
You don't need std::forward
at all here. Except for int&& i
in the second overload, your parameters all declared as non-const lvalue references, so you can't pass an rvalue to any of them. And in the int&&
overload, if you want to call the lvalue function from the rvalue function, you can just name the parameter i
, because a name is always an lvalue.
template <typename T>
bool func(T& data, int& i) {
i = 1;
cout << "it worked!" << endl;
}
template <typename T>
bool func(T& data, int&& i) {
return func(data, i);
}
template <typename T>
bool func(T& data) {
return func(data, 0);
}
If you'd like to remove a function, note that it's actually the int&
that does something effectively different: it changes i
to 1. The rvalue overload technically also does, but something that has been passed as an rvalue should generally be ignored after that point, so callers should only count on func(T&, int&&)
to print the message to cout
. And to take an int
that isn't an lvalue... just take an int
.
template <typename T>
bool func(T& data, int& i) {
i = 1;
cout << "it worked!" << endl;
}
template <typename T>
bool func(T& data, int i=0) {
return func(data, i); // effect on i is ignored.
}
// Okay to pass a const int?
template <typename T>
bool func(T&, const volatile int&&) = delete;
That third deleted template preserves one behavior of your original code, though it's not clear if you actually want that behavior or not. In the function
void test() {
DataClass d;
const int n = 5;
func(d, n);
}
... the original code would have failed to compile, since a const int
lvalue can't bind to either int&
or int&&
. But the change to a parameter of simply int
would allow this test
to compile, by making a copy of n
to be the plain int
parameter. Then the change to that int i
just gets discarded, even though you gave n
to the function. The deleted template is a better match for the const int
lvalue n
, so it would cause test
to fail to compile. If you do want the behavior where func(d, n)
is valid but has no effect on n
, just take out that deleted template.