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
?
The act of creating a new definition of a function, class, or member of a class from a template declaration and one or more template arguments is called template instantiation. The definition created from a template instantiation is called a specialization.
An explicit specialization of a function template is inline only if it is declared with the inline specifier (or defined as deleted), it doesn't matter if the primary template is inline.
To instantiate a template function explicitly, follow the template keyword by a declaration (not definition) for the function, with the function identifier followed by the template arguments. template float twice<float>(float original); Template arguments may be omitted when the compiler can infer them.
There is no semantic difference between class and typename in a template-parameter. typename however is possible in another context when using templates - to hint at the compiler that you are referring to a dependent type. §14.6.
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.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With