The template function encountered an error when passed in a numeric literal, but string literals didn't
I'm writing this code
#include <iostream>
#include <string>
template <typename T>
void Print(T& value)
{
std::cout << value << std::endl;
}
int main()
{
Print("Hello");
Print(1);
}
And when compiling, compilers told an error that “void Print<int>(T &)' : cannot convert argument 1 from 'int' to 'T &'
". But the Print("Hello")
didn't get an error. Why is that?
And I changed Print()
function to
void Print(T value)
{
std::cout << value << std::endl;
}
It worked. But I don't understand why the former code didn't work.
Case 1
Here we consider how Print(1);
works.
In this case, the problem is that 1
is an rvalue and you're trying to bind that rvalue to a lvalue reference to nonconst T
( that is, T&
) which is not possible and hence the error. For example you cannot have:
void Print(int &value)
{
std::cout << value << std::endl;
}
int main()
{
Print(1);// won't work
}
Solution
So to solve your problem you can use a lvalue reference to const T
(that is const T&
) that can be bind to an rvalue, as shown below:
template <typename T>
void Print(const T& value)//note the const added here
{
std::cout << value << std::endl;
}
int main()
{
Print(1); //works now
}
Alternatively, you can also make the parameter as a rvalue reference to nonconst T
(that is T&&
).
template <typename T>
void Print(T&& value)//note the && added here
{
std::cout << value << std::endl;
}
int main()
{
Print(1); //this works too
}
Case 2
Here we consider the statement Print("Hello");
In this case, "Hello"
is a string literal and has the type const char [6]
. Also, the string literal "Hello"
is an lvalue.
And we know that we can bind an lvalue to a lvalue reference to nonconst T
(that is T&
). So there is no error in this case. Also note that in this case the T
is deduced to be const char [6]
.
Note
In case 2 above (Print("Hello");
) there is no type decay because the argument is passed by reference and not by value.
Because of this:
test.cpp:45:11: error: cannot bind non-const lvalue reference of type 'int&' to an rvalue of type 'int'
45 | Print(1);
| ^
So convert it to a universal reference:
template <typename T>
void Print( T&& value ) // notice &&, that's a universal reference, not an rvalue ref
{
std::cout << value << std::endl;
}
1
is an rvalue, and so cannot bind to T&
. It can bind to const T&
though.
That is,
void Print(const T& value)
or
void Print(T&& value)
are the fixes.