「is_complete」テンプレートの書き方
-
06-07-2019 - |
質問
この質問に答えた後、Boostでis_complete
テンプレートを見つけようとしましたライブラリと私は、Boost.TypeTraitsにはそのようなテンプレートがないことに気付きました。 Boostライブラリにそのようなテンプレートがないのはなぜですか?どのように見えるべきですか?
//! Check whether type complete
template<typename T>
struct is_complete
{
static const bool value = ( sizeof(T) > 0 );
};
...
// so I could use it in such a way
BOOST_STATIC_ASSERT( boost::is_complete<T>::value );
上記のコードは、不完全な型にsizeof
を適用することは違法であるため、正しくありません。良い解決策は何ですか?この場合、SFINAEを何らかの形で適用することは可能ですか?
まあ、この問題は一般的に ODRルールに違反しない限り解決できませんでした。しかし、プラットフォーム固有のソリューションがあります。
解決
Alexey Malistovによる回答は、MSVCで若干の修正を加えて使用できます。
namespace
{
template<class T, int discriminator>
struct is_complete {
static T & getT();
static char (& pass(T))[2];
static char pass(...);
static const bool value = sizeof(pass(getT()))==2;
};
}
#define IS_COMPLETE(X) is_complete<X,__COUNTER__>::value
残念ながら、 COUNTER の定義済みマクロは標準の一部ではないため、すべてのコンパイラで動作するわけではありません。
他のヒント
template<class T>
struct is_complete {
static T & getT();
static char (& pass(T))[2];
static char pass(...);
static const bool value = sizeof(pass(getT()))==2;
};
少し遅いかもしれませんが、これまでのところ、完全型と抽象型の両方で機能するC ++ 11ソリューションはありませんでした。
だから、ここにいます。
VS2015(v140)では、g ++ <!> gt; = 4.8.1、clang <!> gt; = 3.4、これは機能しています:
template <class T, class = void>
struct IsComplete : std::false_type
{};
template <class T>
struct IsComplete< T, decltype(void(sizeof(T))) > : std::true_type
{};
Bat-Ulzii Luvsanbatに感謝: https://blogs.msdn.microsoft.com/vcblog/2015/12/02/partial-support-for-expression-sfinae-in-vs-2015-update -1 /
VS2013(V120)の場合:
namespace Details
{
template <class T>
struct IsComplete
{
typedef char no;
struct yes { char dummy[2]; };
template <class U, class = decltype(sizeof(std::declval< U >())) >
static yes check(U*);
template <class U>
static no check(...);
static const bool value = sizeof(check< T >(nullptr)) == sizeof(yes);
};
} // namespace Details
template <class T>
struct IsComplete : std::integral_constant< bool, Details::IsComplete< T >::value >
{};
これはインターネットからヒントを得たもので、静的テンプレートタイプ名Tは完全ではありませんか?
このようなis_complete
タイプの特性を実装することはできないと思います。 @Alexeyによって指定された実装は、G ++ 4.4.2およびG ++ 4.5.0でコンパイルに失敗します。
エラー:<!>#8216; static char(<!> amp; is_complete :: pass(T))[2] [with T = Foo] <!>#8217;
の引数1の初期化
私のMacでは、G ++ 4.0.1でis_complete<Foo>::value
を評価するとstruct Foo;
は不完全で、true
になります。これはコンパイラエラーよりもひどいです。
T
は、翻訳単位に応じて、同じプログラム内で完全なものと不完全なものの両方が可能ですが、常に同じタイプです。結果として、上記でコメントしたように、is_complete<T>
も常に同じタイプです。
したがって、 ODR を尊重する場合、__COUNTER__
を別のものとして評価することはできません。使用場所に応じた値。それ以外の場合は、ODRが禁止するis_complete<T, int>
の定義が異なることを意味します。
編集:受け入れられた答えとして、私自身がIS_COMPLETE
マクロを使用して<=>マクロが使用されるたびに異なる<=>タイプをインスタンス化するソリューションをハックしました。ただし、gccでは、そもそもSFINAEを動作させることができませんでした。
これを解決するには、テンプレートの定義を変更しようとするとODRルールに違反するため、特性テンプレートのデフォルト引数で計算を実行する必要があります(__COUNTER__
とnamespace {}
の組み合わせはODRを回避できます)。 / p>
これはC ++ 11で記述されていますが、最近のC ++ 11互換コンパイラのC ++ 03モードで動作するように調整できます。
template< typename t >
typename std::enable_if< sizeof (t), std::true_type >::type
is_complete_fn( t * );
std::false_type is_complete_fn( ... );
template< typename t, bool value = decltype( is_complete_fn( (t *) nullptr ) )::value >
struct is_complete : std::integral_constant< bool, value > {};
テンプレートの名前が指定されている場所でデフォルト引数が評価されるため、異なる定義間でコンテキストを切り替えることができます。使用ごとに異なる専門化と定義の必要はありません。 true
とfalse
に1つずつ必要です。
ルールは<!>#167; 8.3.6 / 9に記載されており、これは関数のデフォルト引数とデフォルトのテンプレート引数に等しく適用されます:
デフォルトの引数は、関数が呼び出されるたびに評価されます。
ただし、テンプレート内でこれを使用すると、ODRに違反することはほぼ確実です。不完全な型でインスタンス化されたテンプレートは、完全な型でインスタンス化された場合と異なる動作をしてはなりません。個人的にはstatic_assert
に対してのみこれが必要です。
ちなみに、この原則は、別の方法でテンプレートを使用してオーバーロードする<=>の機能を実装します。
関係のない質問に対する答え(私からは与えられていない)がis_complete<T>
テンプレートの解決策を提供することを示すために、ただあいさつします。
答えはこちらです。誤ってクレジットを取得しないように、下に貼り付けません。
標準では、不完全な型のsizeofが0になることを保証するものは見つかりません。ただし、Tがある時点で不完全であるが、その翻訳単位で後で完了する場合、すべての参照が必要です。 TからTは同じ型を参照します-したがって、私が読んだように、テンプレートが呼び出された場所でTが不完全な場合でも、その翻訳単位のどこかでTが完了した場合は完了したと言う必要があります。