【问题标题】:Additional constructor for templated type for <char> only仅适用于 <char> 的模板类型的附加构造函数
【发布时间】:2015-09-08 23:42:09
【问题描述】:

我有一个带有一个模板参数“T”的模板类。我希望这个类有一个构造函数,但如果 T 是 char 只是有一个额外的构造函数“const char *”作为参数。

template <typename T>
class Container
{
public:
    // Construct a container from an array of T's
    Container(const T* data, int count);

    // For char only construct a container from a nul terminated string
    // I *only* want this constructor to be valid when T is char
    Container(const T* data);
};

有什么办法可以做到这一点,让我可以做到:-

const char* init = "Hello";
Container<char> data = init;

会编译但是

const int init[] = {1, 3, 4};
Container<int> data = init;

不会编译。

【问题讨论】:

标签: c++ templates


【解决方案1】:

您可以将其包装在构造函数模板中并使用 SFINAE:

template <typename U=T,
          typename = std::enable_if_t<std::is_same<U, char>::value>>
Container(const U* )
{
    ...
}

这样,对于Container&lt;char&gt;,这个构造函数是可行的,但对于Container&lt;int&gt;,它将是不正确的并从重载集中删除。

一个较小的选择只是:

Container(const char* ) {
    static_assert(std::is_same<T, char>::value, "!");
}

这仍然会在您需要时为您提供const char* 构造函数,而不会为其他Ts 提供const T* 构造函数。但是我们仍然有std::is_constructible&lt;Container&lt;int&gt;, const char*&gt; - 这可能会破坏其他逻辑。所以坚持使用 SFINAE。

【讨论】:

  • 这种方式的问题是他的模板构造函数有两个参数,而对于char模板的情况,他想要一个参数。不能这样。这需要模板专业化。
  • @SamVarshavchik 那是……不是真的。构造函数确实使用了一个参数。它有两个模板参数的事实是无关紧要的。 Demo
【解决方案2】:

你也可以使用Static Assertion

#include <type_traits>

template <typename T>
void Foo(T) {
    static_assert(std::is_same<T, const char*>::value, "T must be const char *");
}

int main(int argc, char* argv[])
{
    const char* c = "c";
    Foo(c); // compile
    Foo(1); // don't compile
}

【讨论】:

    【解决方案3】:

    你可以为char编写构造函数:

    template <> Container<char>::Container(const char* data) {}
    

    示例如下:

    ideone

    【讨论】:

    • AFAIK,您不能在专业化中更改签名
    • 问题:“有什么办法 A)编译(当然工作)B)不编译。”答:看例子。但是投反对票的人甚至都懒得看。
    • 您的解决方案是只为char 版本提供定义,并在非char 情况下出现链接器错误。
    • 链接器错误不是指示无效类型模板参数的最佳方式
    • @PiotrSkotnicki 他没有更改签名,他专门为Container(const T* data) 其中T = char
    【解决方案4】:

    这称为模板特化:

    template<>
    class Container<char>
    {
    public:
        Container(const char *);
    
        // ... the rest of the class declaration
    };
    

    现在,从所有意图和目的来看,这实际上是创建一个单独的类。除了构造函数之外,您还必须编写和声明它的所有其他方法。这可能需要对您的主模板类进行大量代码重复,但通常可以借助通用继承和/或基类来组织一些东西。

    一切就绪:

    Container<int>
    

    将解析为您的主模板,并且

    Container<char>
    

    为您提供专业课程。专业化本质上是根据模板参数选择几个替代模板之一。

    【讨论】:

    • 不幸的是,当特定模板参数的构造函数或方法的签名必须不同时,除了模板特化之外别无选择。
    • 实际上有很多替代方案,都涉及模板元编程。只要您手头有一个好的元编程库来删除样板代码,它甚至是无痛的。
    • 一个简单的替代方案是 SFINAE。我在实际代码中使用它效果很好。
    • @Gombat:您仍然可以创建一个(私有)基类,它只包含专业化。
    • @Jarod42:是的,在某些情况下这也是一个很好的解决方案,例如c++11之前的函数没有模板默认参数。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-05
    • 1970-01-01
    • 2019-03-14
    • 2022-01-16
    • 1970-01-01
    相关资源
    最近更新 更多