3

次の関数について考えてみます。

template <typename Type>
void f(const Type& x)

std::tuple渡された型が空であるか空であるかにかかわらず、その中で(特殊化せずに)特別なことをしたいと思いますstd::array。nu要素のタプルの場合、使用できますstd::is_same<Type, std::tuple<>>::valueが、ゼロ要素配列を検出するためにどのようなトリックを使用できますか?

(私は別の関数やクラスの作成を必要としないソリューションを探しています...)

4

2 に答える 2

6

std::tuple_sizeでも動作するので、を使用できますstd::arrayここを参照してください。単に使用してください:

std::tuple_size<Type>::value == 0

Typeが空か空std::tuple<>かを確認しますstd::array<T,0>


Type上記の場合、どちらでもstd::tupleない場合に何が起こるかという疑問が残りstd::arrayます。私が見る一般的なアプローチはこれです:

constexpr bool IsNotTupleOrArray =
  !std::is_class<Type>::value ||
  std::is_same<Type,ExcludeThisClass>::value ||
  sizeof(Type)>1 || // non-portable, works for GCC 4.8+
  ...;

std::conditional< IsNotTupleOrArray,
                  std::false_type,
                  std::tuple_size<Type> >::type::value;

これは基本的に、他のタイプを明示的に除外する必要があることを意味します。たとえば、ポインタなどのis_class<Type>すべての基本的なタイプを除外します。int

于 2013-03-19T21:27:39.000 に答える
3

専門化せずにやりたくないのなら、オーバーロードでやりませんか?;)関数テンプレートを特殊化することは時々(いいえ、しばしば)悪い考えです。SFINAEのトリックを使用すると、これら2つの条件が当てはまる場合にのみ選択されるオーバーロードを作成することは難しくありません。

しかし、それはあなたがやりたいことではない、とあなたが叫んでいるのをすでに聞いています。あなたが望んでいたのは、条件が真の場合に実行されるif内部のある種のものと、条件が偽の場合に実行される対応するブランチです。f()else

ただし、これは静的な場合ではなく、通常の実行時であることに注意してください。つまり、コンパイラはコンパイル時に、これら2つのブランチのいずれかが実行されないことを100%確実に認識します(およびおそらくそれについて迷惑な警告を発するでしょう)が、両方のブランチを解析し、それらが合法であることを証明する必要があります。

実際には、これは、コンパイル可能にするために特定のタイプT(またはのプロパティ)に依存するステートメントを配置できないことを意味します。Tたとえば、次のコードでは、compile_time_expressionTにメンバー関数があるfoo()かメンバー関数があるかを判別しbar()ます。

T obj;
if (compile_time_expression)
{
    obj.foo();
}
else
{
    obj.bar();
}

ただし、その特定のメンバー関数とメンバー関数の両方Tがない場合、上記はコンパイルされません。ここにあるのはランタイムであるため、コンパイラは両方のブランチを解析し、両方であることを確認する必要がありますコンパイル可能-そして、実行されることのないものを最適化する可能性があります。foo()bar()if

残念ながら、C++にはstaticif(まだ?)などの構造がないため、オーバーロード+SFIN​​AEがこの問題に取り組む正しい方法です。

于 2013-03-19T21:09:42.537 に答える