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,整个事情都会被编译。如果 data
,T
的实例,无法与 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)
}
}
};
或者重构您的代码,记住模板完全是 模板 并且在实例化时它将为其在 any 上的参数生成代码 它定义的代码分支。
我正在尝试为 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,整个事情都会被编译。如果 data
,T
的实例,无法与 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)
}
}
};
或者重构您的代码,记住模板完全是 模板 并且在实例化时它将为其在 any 上的参数生成代码 它定义的代码分支。