2017-06-03 13 views
7

特定の型が完全型であるかどうかを確認する方法.cppif-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; 
} 

に動作し、私はそれではなく、どこにでもその実装自分の書き込みが使用可能になるまでvoid_tと同じ効果のためにstd::enable_if_tを使用したいです。

ODRについての他の回答も見てください。彼らはこれを使用する前に考慮すべき有効なポイントを提示します。

+0

ここでは、2つの状況(完全なものとそうでないものの2つの状況)で、ODR違反のためにあなたのプログラムが病気になり、診断が必要でないと思われると評価します。私はこれがクロスコンパイル単位でもあると信じています。だから、もし「ストレッチ」すれば、「作品」はちょっと見えます。 – Yakk

+0

@Yakkノートが追加されました – Curious

2

ODRというルールがC++にあります。このルールの基本は(私の理解から)、何かがあなたが望むだけ多くの宣言を持つことができますが、ただ一つの定義です。それはシンプルなようですが、テンプレートとインライン関数で、それを壊すのはかなり簡単です。

テンプレートでは、複数の定義が不可避です。同じテンプレートのインスタンス化は、それを使用するすべての翻訳単位で行われます。それは1つの定義ルールに反しているようですが、インラインおよびテンプレート化エンティティの場合、ルールが拡張されます。ここcppreference上の段落です:

限り、各 定義は 次の各の、異なる翻訳単位で表示されるように、プログラムに複数の定義が存在することができます:クラス型、列挙型、インラインクラス 外部リンク付きのインライン変数(C++ 17以降)、クラス テンプレート、非静的関数テンプレート、クラスの静的データメンバ テンプレート、クラステンプレートのメンバ関数、部分テンプレート 特化、以下のすべてが真である限り長く:

  • 各定義は、(典型的には同じヘッダファイルに表示される)トークンの同一の配列からなる内部または付き定数ことを除いて、各定義は(過負荷解像度後に)同じエンティティを見つける内から

  • ネームルックアップを リンケージは、 ODRが使用されていない限り、異なるオブジェクトを参照することができ、すべての定義で同じ値を持ちます。

  • 言語結合(定義内で定義されたものを参照する場合を除き)同じである変換、割当て、および割当て解除機能を含む

  • オーバーロードオペレータは、各
    定義と同じ機能を指す(例えばインクルードファイルは、定義が暗黙的に宣言されたコンストラクタを持つクラスは、すべての翻訳のためである場合は、上記の

  • 3つのルールがそれぞれの定義

  • で使用されるすべてのデフォルト引数に適用されます)extern "C"ブロックの内側ではありませんベースとメンバー

  • 定義はテンプレートのためであれば

    に同じコンストラクタ を呼び出す必要がありますODR-使用されているユニットは、すべてのこれらの要件は で定義し、依存名の時点で両方の名前に適用されますインスタンス化のポイント

これらの要件がすべて満たされている場合、プログラムは、 のように動作し、プログラム全体で1つの定義のみが存在します。それ以外の場合は、動作が定義されていません。

要するに、機能テンプレートが一部の翻訳単位でわずかに異なるものに展開されると、UBランドになります。あなたのプログラムは長い間働くかもしれないし、最適化のようなコンパイルオプションを変更すると突然クラッシュするので、ODR違反をデバッグすることは最悪です。

特定のケースでは、タイプが完全であるかどうかを検出して、関数の定義を変更する必要があります。いくつかの場所では、完全なタイプのがその関数をインスタンス化するので、その関数の複数の異なる定義に終わるでしょう。

マクロにも注意してください。一部の翻訳でのみマクロ定義が変更され、そのマクロをテンプレートまたはインライン関数で使用すると、関数は全く同じトークンではないため、ODRに違反します。


ここで私は他の回答も実際に有用であることを認めます。タイプが完全であるかどうかを検出することは、全く役に立たないわけではありません。私は自分のコードで使っています。私はstatic_assertという素晴らしい診断を提供するために使っています。これはSTLのいくつかの実装でさえ(GCCのSTLのunique_ptrデストラクタ)行います。

+0

関数テンプレートが必要ないと思います: 'is_incomplete 'の検出イディオムはすべての翻訳単位で* same8でなければなりません。これは、どこにでもどこにでも不完全であれば、OPの設計に根本的に欠陥があることを意味するため、これが正当なものであることを忘れないでください。 – Yakk

関連する問題