if-else取决于T是否是完整类型

时间:2017-06-03 03:54:11

标签: c++ templates c++14 incomplete-type

如何检查某个类型在某个.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();*/
    }
};

demo

当我想过早地优化我的自定义智能指针中的删除功能时,它会很有用。

任何人都可以确认这是不可能的吗? 我不是要求解决方法(但我不介意) - 这个问题只是我的好奇心。

2 个答案:

答案 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析构函数)。