问题
I'm trying to create a specialized constructor for std::string
arguments, but the other one is always used when I call it with a string argument.
struct Literal : Expression
{
template <typename V>
Literal(V val)
{
value = val;
}
};
template <>
Literal::Literal(std::string const& val)
{
value = val.c_str();
}
It doesn't matter if both are defined inside the class, both outside the class, or like in the posted example only the specialization is defined outside the class: When called with std::string
, the assignment value = val
gives a compiler error.
How do I correctly specialize this constructor template for std::string
?
回答1:
You don't.
You should overload the constructor: Literal(const std::string&)
, which you can do in the struct
declaration.
The compiler always tries to match non-template overloads before template ones.
回答2:
According to the standard, 14.8.2.1 Deducing template arguments from a function call [temp.deduct.call] where P
is the template parameter and A
is the function-call argument in that position:
2 If P is not a reference type:
If A is an array type, the pointer type produced by the array-to-pointer = standard conversion ([conv.array]) is used in place of A for type deduction; otherwise,
If A is a function type, the pointer type produced by the function-to-pointer standard conversion ([conv.func]) is used in place of A for type deduction; otherwise,
If A is a cv-qualified type, the top-level cv-qualifiers of A's type are ignored for type deduction.
If P is a cv-qualified type, the top-level cv-qualifiers of P's type are ignored for type deduction. If P is a reference type, the type referred to by P is used for type deduction. [...]
So given
std::string s{"hello"};
const std::string& sr{s};
Literal l(sr);
A
(sr) is const std::string&
but the constness is not considered, so the compiler considered std::string
. This matches your
template <typename V>
Literal(V val)
{
value = val;
}
and so it uses this specialization. If you had specialized
template<>
Literal(std::string val)
the compiler would find this specialization, and this is probably what you will have to do and use move semantics.
#include <iostream>
#include <string>
struct S {
template<typename T>
S(T t) { std::cout << "T t\n"; }
std::string value_;
};
template<>
S::S(std::string value) {
std::cout << "string\n";
value_ = std::move(value);
}
template<>
S::S(const std::string&) {
std::cout << "const string&\n";
}
int main() {
S s1(42);
std::string foo{"bar"};
const std::string& foor = foo;
S s2(foo);
S s3(foor);
}
http://ideone.com/eJJ5Ch
回答3:
Many times, when overload is a solution, people try to define full specialization. But overload could be a much better solution. In your case, I would create a new constructor with the string parameter. Remember that only base template is considered in overload resolution. The following article is a good reference to understand this idea: http://www.gotw.ca/publications/mill17.htm
UPDATE:
Anyway, to improve my answer, you could try the following base template constructor:
template <typename V>
Literal(V const& val)
{
value = val;
}
来源:https://stackoverflow.com/questions/38663166/c-constructor-template-specialization