2017-06-03 34 views
7

如何檢查某個類型是否在某個.cpp中是完整類型?if-else取決於T是否是一個完整的類型

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

時,我想過早優化刪除功能,在我的自定義智能指針,將是有益的。

任何人都可以確認這是不可能的嗎?
我不是要求解決方法(但我不介意) - 這個問題只是我的好奇心。

+0

我希望這可以工作,但可惜它不... https://ideone.com/nEGsZu – Curious

+0

你可以根據類型是否完整來做工作,但你的模板必須總是評估相同的事情,或將有違反ODR。 –

+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的答案。他們提出了一個有用的觀點,你應該在使用之前考慮這一點。

+0

現在評估在兩種情況下,一種情況是完整的,另一種情況是不完全的情況,並且由於ODR違規,我懷疑您的程序生病了,不需要診斷。我相信這甚至是交叉編譯單元。所以「作品」似乎有點延伸。 – Yakk

+0

@Yakk note added – Curious

2

C++中有一個叫做ODR的規則。這個規則的基本原理(從我的理解中)是某件事物可以擁有儘可能多的聲明,但只有一個定義。看起來很簡單,但使用模板和內聯函數很容易打破它。

使用模板,多重定義是不可避免的。實例化相同的模板將在所有使用它的翻譯單元中發生。它似乎違背了一個定義規則,但對於內聯和模板實體,該規則得到了擴展。這裏有cppreference一個段落:

可以有一個程序一個以上的定義,只要每個 定義出現在不同的翻譯單元,每個 的以下內容:類類型,枚舉類型,內聯函數與外部連接 鏈接內聯變量與外部鏈接(自C++ 17以來),類 模板,非靜態函數模板,類的靜態數據成員 模板,類模板的成員函數,部分模板 專業化只要滿足以下所有條件:

  • 每個定義包括令牌的相同序列的(典型地,出現在同一頭文件)

  • 名稱查找從內的每個定義查找相同的實體(過載分辨率後),除了與內部或常量 只要它們不是 ODR-使用並且在每個定義中具有相同的值,則不會引用不同的對象。

  • 重載操作符,包括轉化,分配和釋放函數指的是相同的功能從每個
    定義(除非指一個定義內定義)

  • 的語言連接是相同的(例如包括文件不是一個extern "C"塊內)

  • 上述三種規則適用於每個定義

  • 使用的每一個默認參數,如果該定義是有隱含聲明的構造函數的類,每一個翻譯單位在那裏ODR使用的必須調用 相同構造的基礎和成員

  • 如果定義是一個模板,那麼所有這些要求適用於這兩個名字的定義和相關名稱的點 的實例化點

如果滿足所有這些要求,程序的行爲就好像 在整個程序中只有一個定義。否則, 行爲未定義。

總之,如果任何函數模板在某些翻譯單元中擴展到稍微不同的東西,那麼最終會在UB的土地上出現。相信我,調試ODR違規是最糟糕的,因爲您的程序可能會工作很長時間,並且在更改某些編譯選項(例如優化)時突然崩潰。

在你的特定情況下,你想檢測一個類型是否完整或者不需要改變函數的定義。由於在某些地方你可能有一個完整的類型實例化該函數,你將最終得到多個不同的函數定義。

對於宏也要小心。如果某些宏定義僅在某些翻譯中發生變化,並且您在模板或內聯函數中使用該宏,則會違反ODR,因爲該函數不會包含完全相同的標記。


現在,我承認其他答案也確實有用。檢測類型是否完整並不完全沒有用處。我在我的代碼中使用它。我用它來提供static_assert的很好的診斷,甚至STL的一些實現(GCC的STL中的unique_ptr析構函數)。

+0

我不認爲你需要一個函數模板:'is_incomplete '的檢測方式必須是每個翻譯單元中的* same8。這意味着這是合法的,如果它在任何地方或任何地方都是不完整的,這意味着OP的設計從根本上是有缺陷的。 – Yakk

相關問題