如何检查某个类型在某个.cpp
中是否为完整类型?
template<class T>class Test{
//some fields
void(*functor)(T*) =[](T*){};
//^ will be written by some .cpp that can access T as complete-type
T* t=nullptr;
void fComplete(){
delete t; //faster
/** ^ some code that use complete type*/
}
void fForward(){
functor(t); //slower
/** ^ some code that forward declaration is enough*/
}
void f(){
/*if(T is complete type){
fComplete();
}else fForward();*/
}
};
当我想过早地优化我的自定义智能指针中的删除功能时,它会很有用。
任何人都可以确认这是不可能的吗? 我不是要求解决方法(但我不介意) - 这个问题只是我的好奇心。
答案 0 :(得分:7)
这有效
#include <iostream>
#include <type_traits>
using namespace std;
class Incomplete;
class Complete {};
template <typename IncompleteType, typename = std::enable_if_t<true>>
struct DetermineComplete {
static constexpr const bool value = false;
};
template <typename IncompleteType>
struct DetermineComplete<
IncompleteType,
std::enable_if_t<sizeof(IncompleteType) == sizeof(IncompleteType)>> {
static constexpr const bool value = true;
};
int main() {
cout << DetermineComplete<Complete>::value << endl;
cout << DetermineComplete<Incomplete>::value << endl;
return 0;
}
注意我喜欢使用std::enable_if_t
获得与void_t
相同的效果,直到可用,而不是在任何地方自己编写实现。
注意请查看有关ODR的其他答案。它们提出了一个在使用它之前应该考虑的有效点。
答案 1 :(得分:2)
C ++中有一条名为ODR的规则。这条规则的基础知识(根据我的理解)是,某些东西可以拥有你想要的尽可能多的声明,但只有一个定义。看起来很简单,但是使用模板和内联函数,很容易打破它。
使用模板,多重定义是不可避免的。在使用它的所有翻译单元中将发生相同模板的实例化。它似乎违反了一个定义规则,但对于内联和模板化实体,规则得到了扩展。这是关于cppreference的段落:
程序中可以有多个定义,只要每个定义 定义出现在每个的不同翻译单元中 以下:类类型,枚举类型,带外部的内联函数 链接内联变量与外部链接(自C ++ 17以来),类 模板,非静态函数模板,类的静态数据成员 模板,类模板的成员函数,部分模板 专业化,只要满足以下所有条件:
每个定义由相同的令牌序列组成(通常出现在同一个头文件中)
每个定义中的名称查找找到相同的实体(在重载解析之后),除了内部或内部的常量 没有链接可以指不同的对象,只要它们不是 ODR使用并在每个定义中具有相同的值。
重载运算符,包括转换,分配和释放函数,每个都引用相同的函数 定义(除非引用定义中定义的定义)
语言链接相同(例如,包含文件不在
extern "C"
块内)上述三条规则适用于每个定义中使用的每个默认参数
如果定义是针对具有隐式声明的构造函数的类,则每个使用odr的翻译单元必须调用 基础和成员的构造函数相同
如果定义是针对模板的,那么所有这些要求都适用于定义时的两个名称和相关名称。 实例化的重点
如果满足所有这些要求,程序就像是一样 整个程序中只有一个定义。否则, 行为未定义。
简而言之,如果某些翻译单元中的任何功能模板扩展到略有不同的东西,您最终会进入UB领域。相信我,调试ODR违规是最糟糕的,因为你的程序可能会工作很长时间,并且在更改某些编译选项时会突然崩溃,例如优化。
在您的特定情况下,您希望检测类型是否完整以更改函数的定义。因为在某些地方你可能有一个完整的类型和来实例化那个函数,你最终会得到该函数的多个不同的定义。
也要小心宏。如果某些宏定义仅在某些翻译中发生更改,并且您在模板或内联函数中使用该宏,则会违反ODR,因为该函数不会包含完全相同的标记。
现在,我承认其他答案也确实有用。检测类型是否完整并非完全没用。我在我的代码中使用它。我使用它来提供与static_assert
的良好诊断,甚至STL的一些实现(GCC的STL中的unique_ptr
析构函数)。