13

これは別の質問のフォローアップです。それは同じ問題に言及していますが (私は願っています)、それを説明するためにまったく異なる例を使用しています。その理由は、前の例では実験的な GCC 4.9 のみがコンパイル エラーで失敗したためです。この例では、Clang と GCC 4.8.1 もさまざまな方法で失敗します。Clang は予期しない結果を生成し、GCC 4.8.1 は別のエラー メッセージを報告します。

前の質問への回答は、多かれ少なかれ、コードが有効であり、問​​題は GCC の実験的バージョンにあることを示しています。しかし、この結果は私をもう少し懐疑的にします。私は何ヶ月もの間、関連している(または同じ)と思われる問題に悩まされてきましたが、具体的な例を示すのはこれが初めてです。

だから、ここにいくつかのコードがあります。まず、可変個引数のテンプレート エイリアス メタ関数で指定された任意のテストに SFINAE を適用する一般的なコードF:

#include <iostream>
using namespace std;

using _true  = integral_constant <bool, true>;
using _false = integral_constant <bool, false>;

template <typename T> using pass = _true;

template <template <typename...> class F>
struct test
{
    template <typename... A> static _false           _(...);
    template <typename... A> static pass <F <A...> > _(int);
};

template <template <typename...> class F, typename... A>
using sfinae = decltype(test <F>::template _<A...>(0));

次に、指定されたクラスが という名前の型を定義しているかどうかを確認する特定のテストtype:

template <typename T> using type_of  = typename T::type;
template <typename T> using has_type = sfinae <type_of, T>;

最後に、例:

struct A { using type = double; };

int main()
{
    cout << has_type <int>() << ", ";
    cout << has_type <A>()   << endl;
}

期待される結果は です0, 1。クランは言い0, 0ます。GCC 4.8.1 は言う

tst.cpp: In substitution of ‘template<class T> using type_of = typename T::type [with T = A ...]’:
tst.cpp:15:51: required from ‘struct test<type_of>’
tst.cpp:19:67: required by substitution of ‘template<template<class ...> class F, class ... A> using sfinae = decltype (test:: _<A ...>(0)) [with F = type_of; A = {T}]’
tst.cpp:24:58: required from here
tst.cpp:23:56: error: ‘A ...’ is not a class, struct, or union type
  template <typename T> using type_of = typename T::type; 
                                                        ^

そしてGCC 4.9は言う

tst.cpp:19:67:   required by substitution of ‘template<template<class ...> class F, class ... A> using sfinae = decltype (test:: _<A ...>(0)) [with F = type_of; A = {T}]’
tst.cpp:24:58:   required from here
tst.cpp:15:51: error: pack expansion argument for non-pack parameter ‘T’ of alias template ‘template<class T> using type_of = typename T::type’
  template <typename... A> static pass <F <A...> > _(int);
                                                   ^

(行番号は異なる場合があります)。したがって、さまざまな方法ですべてが失敗します。

さて、ここに回避策があります。メタ関数carは、指定されたパックから最初の型を選択し、テストは として再定義されtype_of2、現在は可変個です:

template <typename... T> struct car_t;
template <typename... T> using  car = type_of <car_t <T...> >;

template <typename T, typename... Tn>
struct car_t <T, Tn...> { using type = T; };

template <typename... T> using type_of2  = typename car <T...>::type;
template <typename T>    using has_type2 = sfinae <type_of2, T>;

int main()
{
    cout << has_type2 <int>() << ", ";
    cout << has_type2 <A>()   << endl;
}

これで、3 つのコンパイラすべて0, 1が期待どおりに動作するようになりました。興味深いことに、GCC のどのバージョンでhas_typeも (使用しない場合でも)削除してhas_type2、. そうしないと、同様のエラーが発生します。

まとめ: フォームの可変個引数のテンプレート パラメーターを期待する 1 つのテンプレートに問題があることがわかります。

template <typename...> class F

ここで、実際には、次の形式の非可変個のテンプレート エイリアスを入力として指定します。

template <typename T> using alias = // ... anything including T or not

Fそして最後に、可変個引数であるかのように呼び出します。

F <A...>

これまでのところ、これは有効であるという意見がありますが、現在、3 つのコンパイラが反対しているようです。もう一度質問です。それは有効ですか?

私にとっては、これが有効であるという仮定に基づいて既存のコードのファイルが数十あり、とにかく再設計が必要なので (これらのコンパイラには実際的な問題があるため)、正確な再設計は答えによって異なります。

4

2 に答える 2

2

これは、上記のコードが有効かどうかという質問には答えませんが、質問をした直後に実験して見つけたかなりの回避策であり、共有するのに役立つと思います.

必要なのは、次の定義だけです。

template <template <typename...> class F>
struct temp { };

template <typename... A, template <typename...> class F>
F <A...> subs_fun(temp <F>);

template <template <typename...> class F, typename... A>
using subs = decltype(subs_fun <A...>(temp <F>()));

次に、F <A...>問題が発生する場所を に置き換えますsubs <F, A...>。それでおしまい。理由は説明できませんが、これまでのところすべてのケースで機能しています。

たとえば、質問のSFINAEの例では、行を置き換えるだけです

template <typename... A> static pass <F <A...> > _(int);

template <typename... A> static pass <subs <F, A...> > _(int);

これは 1 点のみの変更で、残りのコードはすべて同じままです。として使用されるすべてのテンプレート メタ関数を再定義またはラップする必要はありませんF。これが実際のです。

が実際に有効であり、最終的にコンパイラがサポートする場合F <A...>、変更が最小限であるため、元に戻すのは簡単です。

SFINAE テストをわずか 2 行で指定できるため、これは重要だと思います。

template <typename T> using type_of  = typename T::type;
template <typename T> using has_type = sfinae <type_of, T>;

そして完全に一般的です。通常、このような各テストには少なくとも 10 行のコードが必要であり、 の実装は<type_traits>そのようなコードでいっぱいです。場合によっては、そのようなコード ブロックがマクロとして定義されます。このソリューションでは、テンプレートがその役割を果たし、マクロは必要ありません。

于 2014-03-13T09:35:55.260 に答える
0

状況はかなり標準化されていると思います。C++11 14.3.3/1 は次のように述べています。

テンプレートtemplate-parametertemplate-argumentは、クラス テンプレートまたはエイリアス テンプレートの名前であり、id-expressionとして表されます。

于 2013-11-29T18:19:21.437 に答える