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

use*_*354 2 c++ generics parameters templates types

我有一个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;
  }
}
Run Code Online (Sandbox Code Playgroud)

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

assigning to 'int' from incompatible type 'std::__cxx11::basic_string<char>'
Run Code Online (Sandbox Code Playgroud)

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

Sam*_*hik 8

您无法以您尝试的方式强制转换模板参数:

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;
  }
}
Run Code Online (Sandbox Code Playgroud)

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

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

void MyClass::set(int value)
{
     if (false)
     {
         stringValue_=value;
     }
Run Code Online (Sandbox Code Playgroud)

在这里您尝试设置stringValue,这想必std::string,到int,不会是非常成功的.仅仅因为if条件是错误的,不会使这段代码消失.它仍然必须是有效的C++,即使它永远不会被执行,这也不是有效的C++.这是编译错误的解释.

最新的C++ 17标准中的一些功能将使这些类型的构造实际上成为可能.但是,预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;
}
Run Code Online (Sandbox Code Playgroud)

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


Hen*_*nke 6

编译器错误的原因是if语句的两个分支都将使用模板进行实例化。这显然是一个错误,因为如果T = int没有赋值运算符 to 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;
  }
}
Run Code Online (Sandbox Code Playgroud)

但实际上,不要在这里使用模板(也不要在这里使用 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;
}
Run Code Online (Sandbox Code Playgroud)

有人可能会争辩说,如果您试图涵盖可相互转换的整个类型范围,则 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;
    }
};
Run Code Online (Sandbox Code Playgroud)