【问题标题】:C++ constructor template specializationC++ 构造函数模板特化
【发布时间】:2016-12-04 10:15:54
【问题描述】:

我正在尝试为std::string 参数创建一个专门的构造函数,但是当我使用字符串参数调用它时,总是使用另一个构造函数。

struct Literal : Expression
{    
    template <typename V>
    Literal(V val)
    {
        value = val;
    }
};

template <>
Literal::Literal(std::string const& val)
{
    value = val.c_str();
}

无论是在类内定义还是类外定义都没有关系,或者像在发布的示例中一样,只在类外定义特化:当用std::string调用时,赋值value = val给出一个编译器错误。

我如何正确地为std::string 特化这个构造函数模板?

【问题讨论】:

  • 你没有声明value。这是您将得到的错误:main.cpp:12:9: error: 'value' was not declared in this scope value = val;
  • @HenriqueBarcelos ATL::CComVariant valueExpression 中声明。为了简洁起见,我只发布了相关的模板内容。
  • 我假设value 是在基类中定义的。

标签: c++ templates constructor template-specialization


【解决方案1】:

你没有。

您应该重载构造函数Literal(const std::string&amp;),您可以在struct 声明中执行此操作。

编译器总是尝试在模板重载之前匹配非模板重载。

【讨论】:

  • 没有编辑,我没有看到非模板构造函数?
  • 我的困惑是:他只有一个构造函数,而且它是模板化的,他试图专门化它,所以编译器的选择是在类内模板化声明/定义或它的特化之间 -非模板重载机会在哪里?
  • @kfsone 我刚刚添加了一个非模板化的重载,它现在可以工作了。
  • @kfsone 您只需向未模板化并接受const std::string&amp; 的类添加第二个构造函数。
【解决方案2】:

根据standard, 14.8.2.1 Deducing template arguments from a function call [temp.deduct.call] 其中P 是模板参数,A 是该位置的函数调用参数:

2 如果 P 不是引用类型:

如果A是数组类型,则使用array-to-pointer =标准转换([conv.array])产生的指针类型代替A进行类型推导;否则,

如果A是函数类型,则使用函数到指针标准转换([conv.func])产生的指针类型代替A进行类型推导;否则,

如果 A 是 cv 限定类型,则类型推导时忽略 A 类型的顶级 cv 限定符。

如果 P 是 cv 限定类型,则忽略 P 类型的顶级 cv 限定符进行类型推导。如果 P 是引用类型,则使用 P 所引用的类型进行类型推导。 [...]

给定

std::string s{"hello"};
const std::string& sr{s};
Literal l(sr);

A (sr) 是const std::string&amp; 但不考虑常量,所以编译器考虑了std::string。这符合您的

template <typename V>
Literal(V val)
{
    value = val;
}

所以它使用了这个专业化。如果你有专门的

template<>
Literal(std::string val)

编译器会发现这种特化,这可能是您必须做的并使用移动语义。

#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

【讨论】:

  • 谢谢,这是一个正确的答案!但是,我使用 gcc 工具链没有那种问题。只有在移植到clang时,我才不得不寻找答案。
【解决方案3】:

很多时候,当重载成为一种解决方案时,人们会尝试定义完全专业化。但是过载可能是一个更好的解决方案。在您的情况下,我将使用字符串参数创建一个新的构造函数。 请记住,在重载决议中只考虑基本模板。下面的文章是理解这个想法的一个很好的参考: http://www.gotw.ca/publications/mill17.htm

更新:

无论如何,为了改进我的答案,您可以尝试以下基本模板构造函数:

template <typename V>
Literal(V const& val)
{
    value = val;
}

【讨论】:

    猜你喜欢
    • 2010-12-26
    • 1970-01-01
    • 1970-01-01
    • 2011-02-09
    • 2011-05-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多