C ++模板化类,具有不同的行为,具体取决于类型

时间:2017-03-27 11:43:07

标签: c++ templates

说你有这样的课程:

template <typename T>
class MyClass {
    public:
    void doSomething();
};

在一个方法中,是否有任何方法可以根据类型具有不同的行为:

template <typename T> MyClass::doSomething() {
    //this is what I want:
    std::string value = "17";
    if(T == int) {
         int myInt = atoi(value.c_str());
    } else if(T == std::string) {
         std::string myString = value;
    }
}

有没有简单的方法来实现这一点?
我知道我可以编写一个包装器类,它提供一个带std::string的构造函数,所以我可以使用类似的东西:

T myVar(value);

但如果使用像int之类的简单类型,那将会浪费更多资源,我也想知道if-else-statement能够执行不同的操作。

我会感谢你的回答。 的问候,

tagelicht

3 个答案:

答案 0 :(得分:4)

在C ++ 17中,您可以使用if constexpr(...)

完全达到您想要的效果
template <typename T> MyClass::doSomething() {
    //this is what I want:
    std::string value = "17";
    if constexpr(std::is_same_v<T, int>) {
         int myInt = atoi(value.c_str());
    } else constexpr(std::is_same_v<T, std::string>) {
         std::string myString = value;
    }
}

在C ++ 14中,您可以非常轻松地实现自己的static_if。我在CppCon 2016和Meeting C ++ 2016上给出了a tutorial talk

template <typename T> MyClass::doSomething() {
    //this is what I want:
    std::string value = "17";
    static_if(std::is_same_v<T, int>)
        .then([&](auto) {
             int myInt = atoi(value.c_str());
        })
        .else_if(std::is_same_v<T, std::string>) 
        .then([&](auto) {
             std::string myString = value;
        })();
}

在C ++ 11中,您可能希望使用函数重载模板专门化。前的示例(在评论中更新了Jarod42的建议)

template <typename> struct tag { };

void call_dispatch(...) { } // lowest priority

void call_dispatch(tag<std::string>) 
{ 
    std::string myString = value; 
}

void call_dispatch(tag<int>) 
{ 
    int myInt = atoi(value.c_str()); 
}

用法:

template <typename T> MyClass::doSomething() {
    //this is what I want:
    std::string value = "17";
    call_dispatch(tag<T>{});
}

答案 1 :(得分:3)

尚未举例说明的两种方法是1)标签调度和2)模板专业化

标签发送:

  • 调用使用重载的辅助函数

我们这样做的方法是将一些空的模板化结构定义为轻量级“标记”类型:

template <typename T>
class MyClass {
    public:
    void doSomething();
    private:
    // tag for dispatch
    template<class U>
    struct doSomethingTag{};

...然后定义需要对该标记进行特化的辅助函数:

    void doSomethingHelper(doSomethingTag<std::string>, std::string value);
    void doSomethingHelper(doSomethingTag<int>, std::string value);
};

然后我们可以使用主入口点(doSomething)作为调用专用助手函数的方法:

template <typename T> 
void MyClass<T>::doSomething() {

    //dispatch to helper function
    doSomethingHelper(doSomethingTag<T>{}, "17");
}

Demo

辅助函数如下所示:

template <typename T> 
void MyClass<T>::doSomethingHelper(doSomethingTag<std::string>, std::string value)
{
    int myInt = atoi(value.c_str());
}

template <typename T> 
void MyClass<T>::doSomethingHelper(doSomethingTag<int>, std::string value)
{
    std::string myString = value;
}

这会导致标签结构的无开销,因为辅助函数实际上并不使用tag参数,因此它将被优化掉。如果不是,则构造结构的开销是1字节。 (注:Vittorio Romeo hinted at this in his answer,但他称之为“功能超载”)

模板专业化

只要你完全专注它,你就可以“专门化”你的功能模板(指定类的类型!):

专业化的语法看起来有点奇怪,因为我们将角括号保持为空:<>

template<> 
void MyClass<int>::doSomething() {

    //dispatch to helper function
    std::string value = "17";
    int myInt = atoi(value.c_str());
}

template <>
void MyClass<std::string>::doSomething()
{
    std::string value = "17";
    std::string myString = value;
}

这使您可以保留较小的班级:

template <typename T>
class MyClass {
    public:
    void doSomething();
};

Demo 2

结论:

在你给出的具体例子中,我更喜欢模板专业化。但是,您经常需要进行某种部分特化,这对于成员函数来说非常奇怪,因此通常首选标记调度。

使用C ++ 17,使用constexpr if绝对可以让生活更轻松。

答案 2 :(得分:2)

这个怎么样:

#include <type_traits>

template <typename T> MyClass::doSomething() {
    //this is what I want:
    std::string value = "17";
    if(std::is_same<T,int>::value) {
         int myInt = atoi(value.c_str());
    } else if(std::is_same<T,std::string>::value) {
         std::string myString = value;
    }
}

这是C ++ 11。使用constexpr if的C ++ 17可以使它更好。这里存在的问题是你必须转换非隐式可转换的值。例如,必须将std::string的返回强制转换为int才能进行编译。