如何转换C ++模板参数?

时间:2017-07-24 23:41:48

标签: c++ templates generics types parameters

我有一个C ++函数,如下所示:

template <class T> void MyClass::set(T value) {
  if (std::is_same<T, std::string>::value) {
    stringValue_ = value;
  } else if (std::is_same<T, int>::value) {
    intValue_ = value;
  }
}

但是,我收到了编译错误。显然它认为T的类型总是一个std :: string:

assigning to 'int' from incompatible type 'std::__cxx11::basic_string<char>'

此外,当我尝试将值转换为int(int)值时,我得到了转换错误。此函数的整个要点是获取模板参数值,然后根据参数的实际类型将其分配给类的正确变量。请告诉我我在这里犯的错误或错误。

2 个答案:

答案 0 :(得分:7)

您无法以您尝试的方式投射模板参数:

template <class T> void MyClass::set(T value) {
  if (std::is_same<T, std::string>::value) {
    stringValue_ = value;
  } else if (std::is_same<T, int>::value) {
    intValue_ = value;
  }
}

这里的关键概念是模板函数在实例化时完整地扩展

如果说,对于给定的模板实例,T模板参数是一个整数。然后,该模板大致以下列方式实例化:

void MyClass::set(int value)
{
     if (false)
     {
         stringValue_=value;
     }

您在此尝试将stringValue(可能是std::string)设置为int,并不会非常成功。仅仅因为if条件为假,就不会使这段代码消失。它仍然必须是有效的C ++,即使它永远不会被执行,这也不是有效的C ++。这是编译错误的解释。

最新的C ++ 17标准中的一些功能将使这些类型的构造实际上成为可能。但是,预解决这类问题的一般方法是使用模板专业化:

template<typename T> void MyClass::set(T value);

template<> void MyClass::set<int>(int value)
{
    intValue_=value;
}

template<> void MyClass::set<std::string>(std::string value)
{
    stringValue_=value;
}

或者,完全忘记模板,只需定义两个单独的set()方法。

答案 1 :(得分:3)

编译器错误的原因是if语句的两个分支都将使用模板进行实例化。这显然是一个错误,因为如果T = int没有赋值运算符std::string。 C ++ 17引入了一种在编译时忽略其中一个分支的方法,称为if constexpr

template <class T> void MyClass::set(T value) {
  if constexpr (std::is_same<T, std::string>::value) {
    stringValue_ = value;
  } else if (std::is_same<T, int>::value) {
    intValue_ = value;
  }
}

但实际上,不要在这里使用模板(也不要在这里使用SFINAE!)。只需使用旧的函数重载。

class MyClass
{
    std::string stringValue_;
    int intValue_;
public:
    void set(int);
    void set(std::string);
};

void MyClass::set(int value) {
    intValue_ = value;
}

void MyClass::set(std::string value) {
    stringValue_ = value;
}

有人可能会说,如果你试图涵盖可以相互转换的各种类型,SFINAE实际上是必要的。

class MyClass
{
    std::string stringValue_;
    int intValue_;
public:
    template < typename T, typename std::enable_if< std::is_arithmetic<T>::value, void** >::type = nullptr >
    void set(T value) {
        intValue_ = value;
    }

    template < typename T, typename std::enable_if< std::is_same<T,std::string>::value, void** >::type = nullptr >
    void set(T value) {
        stringValue_ = value;
    }
};