C++ - 模板 is_Pointer 似乎失败的奇怪案例

C++ - Curious case where Template is_Pointer seems to fails

我正在尝试为 BinaryTree Operations 创建项目,以下是 class 原型以及我遇到问题的代码。

BinaryTree.h 文件中的二叉树 Class

template <class T>
class BinaryTree
{
   public:  
       BinaryTree();
       BinaryTree(T);     
};

BinaryTree.cpp

中的构造函数实现
template<class T>
struct isPointer
{
  static const bool value=false;
};

template<class T>
struct isPointer<T*>
{
  static const bool value=true;
};

template<class T>
BinaryTree<T>::BinaryTree():root(nullptr)
{
  //ctor
}

template<class T>
BinaryTree<T>::BinaryTree(T data)
{
  if(isPointer<T>::value == true)
  {
   if(data != nullptr)
   {
      //Do something
   }
  }      
}

BinaryTreeOperations Class 继承BinaryTree Class 其原型定义在BinaryTreeOperations.h

template<class T>
class BinaryTreeOperations:public BinaryTree<T>
{
  public:

  //Constructors
  BinaryTreeOperations();
  BinaryTreeOperations(T);
};

而构造函数在 BinaryTreeOperations.cpp class

中定义
template<class T>
BinaryTreeOperations<T>::BinaryTreeOperations(T data):BinaryTree<T>(data)
{
  //ctor
}

Main.cpp文件中的主要函数

int main()
{
  cout << "Hello world!" << endl;

  BinaryTreeOperations<std::string> b("1");
}

现在 g++ 抛出的错误是

no match for 'operator!=' (operand types are 'std::__cxx11::basic_string' and 'std::nullptr_t')

if(data != nullptr)

在 BinaryTree.cpp class

中定义的 BinaryTree 构造函数中

问题来了。我已经定义了isPointer结构来检查给定的模板是否为指针。但似乎,尽管 T 是 std::string g++ 正在进入 if(isPointer<T>::value == true) 健康)状况。

我不明白我做错了什么?任何形式的指导都将不胜感激。

  if(isPointer<T>::value == true)
  {
   if(data != nullptr)
   {
      //Do something
   }
  }      

这将为每个 T 编译。即使对于给定的 T,表达式 isPointer<T>::value 的计算结果为 false,整个事情都会被编译。如果 dataT 的实例,无法与 nullptr 进行比较,则会导致编译失败。

C++17 引入了 static_if,一个编译时指令,它根据编译时 constexpr 选择性地编译代码块(本身就是一个有争议的补充)。在 C++17 之前,除了 isPointer.

的特化之外,正确执行此操作的唯一方法是使用特化来选择性地编译这段代码。

但是,无论如何,你的编译错误的答案是它等同于:

std::string data;

if (0)
{
   if (data != nullptr)
   {
      // ...
   }
}

问题代码永远不会被访问这一事实不会改变它必须是有效的、可编译的 C++ 代码这一事实。

But it seems, in spite of T being std::string g++ is going in if(isPointer<T>::value == true) condition.

确实如此。将编译整个函数。执行是否能够达到该比较是无关紧要的。

解决方案:编写一个函数模板并为指针重载它。

template<class T>
void do_something(const T&) {
    // do nothing
}

template<class T>
void do_something(T* data) {
    if(data != nullptr) {
        // do something
   }
}

//  ...

template<class T>
BinaryTree<T>::BinaryTree(T data)
{
  do_something(data);
}

C++17 计划引入 constexpr if,这将允许您在单个函数中就地编写编译时条件。

对于这种情况,constexpr if (C++17) 会是一个不错的补充:您的分支将在编译时 对您通过的每个 T 进行评估.

一种可能的解决方法是利用 enable_if and define two constructor versions depending on the result of your isPointer predicate and let SFINAE 发挥作用

template <class T>
class BinaryTree
{
public:
  BinaryTree();

  template<typename TT = T>
  BinaryTree(TT, typename std::enable_if<!isPointer<TT>::value>::type * = nullptr) {

  }

  template<typename TT = T>
  BinaryTree(TT data, typename std::enable_if<isPointer<TT>::value>::type * = nullptr) {
    if (data != nullptr)
    {
      //Do something (this is a pointer)
    }
  }

};

Example

或者重构您的代码,记住模板完全是 模板 并且在实例化时它将为其在 any 上的参数生成代码 它定义的代码分支。