4

コードへの参照がいくつかある質問をしました。

template <typename...>
using void_t = void;

私は一般的にエイリアステンプレートを誤解していると思います:

enable_if_torconditional_tステートメントでエイリアス テンプレートに渡すテンプレート パラメーターを評価しないのはなぜでしょうか。

上記のコードは、enable_if_t一度に複数のテンプレート パラメータを実行するだけですか?

第二に、私は の役割について特定の誤解をしていると思いますvoid_tこのコメントは、C++17 標準が を定義していると述べていますvoid_t。ここに私が得られないものがあります:

void_t勝手な名前じゃないの?template <typename...> using void_t = void;使用する予定の場所を定義する必要がある場合void_t、任意の名前を標準化する意味は何ですか?

4

2 に答える 2

8

リンクされた質問からのバリーの例では:

template<typename T, typename = void>
struct has_to_string
: std::false_type { };

template<typename T>
struct has_to_string<T, 
    void_t<decltype(std::to_string(std::declval<T>()))>
    > 
: std::true_type { };

void_tによって推定される型を変換するために使用されるだけdecltypevoid、デフォルトの引数がプライマリテンプレート定義に一致します。SFINAE はすべてdecltype式によって処理されます。次のように簡単に実行できます。

//use , void() instead of wrapping in void_t
//this uses the comma operator to check the type of the to_string call, then change the type to void
decltype(std::to_string(std::declval<T>()), void())

以前のバージョンははるかに読みやすく、void_t動作する必要はありませんdecltype

が実装で利用可能な場合void_tは、再定義する必要はありません。標準化されると、標準の他のエイリアス テンプレートと同じように利用できるようになります。

このように考えてください:有効なオーバーロードを持つTisの場合、演繹は次のようになります。intstd::to_string

has_to_string<int>->has_to_string<int,void>デフォルトの引数のため。それではhas_to_string、これらの引数での特殊化を探してみましょう。

template<typename T>
struct has_to_string<T, 
    void_t<decltype(std::to_string(std::declval<T>()))>
    > 
: std::true_type { };

さて、それはTいくつかの依存型の部分的な特殊化です。そのタイプを考えてみましょう:

void_t<decltype(std::to_string(std::declval<T>()))>
//std::to_string(int&&) is valid and returns a std::string
void_t<std::string>
//void_t changes types to void
void

これで、専門化は次のようになります。

template<>
struct has_to_string<int,void>
: std::true_type { };

これは のインスタンス化と一致するhas_string<int,void>ため、 からhas_to_string<int>継承しstd::true_typeます。

Tが のときを考えてみてくださいstruct Foo{};。もう一度、その依存型を考えてみましょう。

void_t<decltype(std::to_string(std::declval<T>()))>
//wait, std::to_string(Foo&&) doesn't exist
//discard that specialization

その特殊化を破棄すると、プライマリ テンプレートに戻ります。

template<typename T, typename = void>
struct has_to_string
: std::false_type { };

からhas_to_string<Foo>継承しstd::false_typeます。

于 2015-05-18T11:55:26.070 に答える
3

void_t示されている例は、1 つのユース ケースしか示していないため、実際に何が良いかを示しているとは思いませんが、

template<typename T>
struct has_to_string<T, 
    void_t<decltype(std::to_string(std::declval<T>()))>
    > 
: std::true_type { };

とあまり変わらない

template<typename T>
struct has_to_string<T, 
    decltype(std::to_string(std::declval<T>()), void())
    > 
: std::true_type { };

そして、このステートメントについて:

以前のバージョンははるかに読みやすく、void_t動作する必要はありませんdecltype

読みやすさの利点は非常に小さく、2番目の部分は意味がないと思いますdecltype.機能しない場合、SFINAEは期待どおりに機能します.

void_tより有用な一例は、提案からのものです:

// primary template handles types that have no nested ::type member
template< class, class = void_t<> >
struct has_type_member
: std::false_type { };

// specialization recognizes types that do have a nested ::type member
template< class T >
struct has_type_member<T, void_t<typename T::type>>
: std::true_type { }

ご覧のとおり、主なテンプレートでさえ、void_t専門化と一致するようになったため、読みやすさを向上させるために使用されます。それは厳密には必要ではありませんが、私はそれが好きです。本当の力は、代替案について考えるときに生まれます。がないvoid_tと、特殊化はより複雑になります。

template< class T >
struct has_type_member<T, decltype(typename T::type, void())>
: std::true_type { }

T::type式ではなく型の名前としては機能しません。したがって、必要です

template< class T >
struct has_type_member<T, decltype(std::declval<typename T::type>(), void())>
: std::true_type { }

式全体が長くなり、よりトリッキーになり、処理を忘れたエッジ ケースに悩まされる可能性があります。これはvoid_t本当に役立つところです。他の用途はほんの少しの改善であり、一貫性が向上します。

于 2015-05-19T19:30:25.460 に答える