C++ constructor template specialization

旧巷老猫 提交于 2020-02-02 02:59:39

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!