2016-04-04 4 views
2

SFINAE経由でテンプレートテンプレートパラメータにバインドできるタイプがあるかどうかをテストできますか?SFINAEを使用して、型がテンプレートテンプレートパラメータにバインドできるかどうかを確認します。

私は私は何をしようとすると、最良の次のコード例で説明されていると思う:

#include <iostream> 

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

template<typename T> struct TemporaryBindObject 
{ 
    using type = TemporaryBindObject<T>; 
}; 

template<template<typename...> class Dest> struct TestValidBind 
{ 
    template<typename... Ts> struct toTypesOf 
    { 
     using type = std::false_type; 
    }; 
    template<template<typename...> class Src, typename... Ts> struct toTypesOf<Src<Ts...>, void_t<Dest<Ts...,float>>> 
    { 
     using type = std::true_type; 
    }; 
}; 

template<typename T> struct OneParamStruct{}; 
template<typename T1, typename T2> struct TwoParamStruct{}; 

int main() 
{ 
    using tmp = TemporaryBindObject<int>; 

    std::cout << "Can bind to TwoParamStruct: " << TestValidBind<TwoParamStruct>::toTypesOf<tmp>::type::value << std::endl; 
    std::cout << "Can bind to OneParamStruct: " << TestValidBind<OneParamStruct>::toTypesOf<tmp>::type::value << std::endl; 
} 

まず、私は別のクラスにバインドするテンプレートパラメータintを取るしたいから、一時的なタイプtmpを作りますテンプレート。 TestValidBind<template template type>::toTypesOf<typename>template template parameterに所定のタイプのパラメータをバインドし、追加のタイプ(この例ではfloat)を追加できるかどうかをテストします。

私が欲しいのは、TestValidBind<TwoParamStruct>::toTypesOf<tmp>::typetrue_typeであり、TestValidBind<OneParamStruct>::toTypesOf<tmp>::typefalse_typeであることです。次のエラーで


g++ -std=c++11(5.3.1)でコンパイルしないであるようにコード例:

../test_SFINAE_with_template_binding.cc: In function ‘int main()’: ../test_SFINAE_with_template_binding.cc:34:96: error: ‘TestValidBind<OneParamStruct>::toTypesOf<TemporaryBindObject<int> >::type’ has not been declared

OneParamStruct行が削除された場合レポートfalse_type(間違っています)。

clang++ -std=c++11(3.8.0)の場合、コードはコンパイルされますが、どちらの場合もfalse_typeと報告されます。

これはまったく同じですか?


編集:は、私は、追加のタイプが可能であるかどうかを確認したいことを強調するためにfloatvoidから追加の種類を変更しました。

答えて

2

void_tトリックでは、プライマリテンプレートのパラメータの1つにデフォルトタイプvoidを指定する必要があります。プライマリテンプレート(toTypesOf)をバリデーションする必要はありませんでした。

それは代わりに、ネストされたtypeを有するとfalse_type又はtrue_typeから継承するbool型形質のより慣用的です。 TemporaryBindObjectにネストされたtypeを入れる必要はありません。

template<template<typename...> class Dest> struct TestValidBind 
{ 
    template<template<typename...> class Dest, typename T, typename = void_t<>> struct toTypesOf 
     : std::false_type 
    {}; 
    template<template<typename...> class Dest, template<typename...> class Src, typename... Ts> struct toTypesOf<Dest, Src<Ts...>, void_t<Dest<Ts..., float>>> 
     : std::true_type 
    {}; 
}; 

EDIT:あなたが言うように、これは、G ++では動作しません、そしてそれは、なぜ私にはわからない

TestValidBindは、次のようになります。しかし、我々はそれを単純化し、それが役立つかどうかを見ることができます。実際には、囲み構造体TestValidBindは必要ありません。我々はそれにDestパラメータを移植場合toTypesOfテンプレートは、名前空間スコープで使用できます

template<template<typename...> class Dest, typename T, typename = void_t<>> struct toTypesOf 
    : std::false_type 
{}; 
template<template<typename...> class Dest, template<typename...> class Src, typename... Ts> struct toTypesOf<Dest, Src<Ts...>, void_t<Dest<Ts..., float>>> 
    : std::true_type 
{}; 

これは、G ++で動作します。

DEMO

我々はさらに行くと、あなたが好きなら、それは少し簡単detail名前空間内のすべてのものを置くとエイリアステンプレートでそれをラップすることにより、使用することができます:

namespace detail 
{ 
    template<typename... T> using void_t = void; 

    template<typename... T> struct TemporaryBindObject {}; 

    template<template<typename...> class Dest, typename T, typename = void_t<>> struct toTypesOf 
     : std::false_type {}; 
    template<template<typename...> class Dest, template<typename...> class Src, typename... Ts> struct toTypesOf<Dest, Src<Ts...>, void_t<Dest<Ts...>>> 
     : std::true_type {}; 
} 

template<template<typename...> class Dest, typename... Ts> 
using IsValidBind = typename detail::toTypesOf<Dest, detail::TemporaryBindObject<Ts...>>; 

template<template<typename...> class Dest, typename... Ts> 
using IsValidBindWithFloat = IsValidBind<Dest, Ts..., float>; 

template<template<typename...> class Dest, typename... Ts> 
using IsValidBindWithVoid = IsValidBind<Dest, Ts..., void>; 

std::cout << "Can bind to TwoParamStruct: " << IsValidBindWithFloat<TwoParamStruct, int>::value << std::endl; 
std::cout << "Can bind to OneParamStruct: " << IsValidBindWithFloat<OneParamStruct, int>::value << std::endl; 

今、私たちドンusing tmpが必要です。追加のタイプとして使用するタイプを簡単に変更できる、より一般的なソリューションがあります。ここで

DEMO

+0

有効な修正をいただきありがとうございます。実際には、私の質問に正確に答えていません。私はまだ動作しません(おそらく私の例は、ポイントを強調するために適切に調整されていない)の反対を求めていた。私が試したかったのは、追加のタイプを追加することでした(私は「無効」にしました)。私はこの点を強調するために何かに変更します。 – havogt

+0

私のコメントでは少し速すぎました:私の例(タイプが多すぎます)があなたの修正で今clangで動作しますが、まだg ++ 5で失敗します。あなたの例は両方のコンパイラで動作します。 – havogt

+0

私はあなたの答えに合うように私の質問を調整します(本当にあなたは愚かな間違いを修正したので)、明日新しい質問をします。しかし、私は編集で私の質問を変更していないことに注意しよう! – havogt

1

私はそれを行うだろうかです:

using std::void_t; // or write your own 
template<class T>struct tag{using type=T;}; 
template<template<class...>class Z>struct ztag{ 
    template<class...Ts>using result=Z<Ts...>; 
}; 

namespace details { 
    template<class Src, class Target, class=void> 
    struct rebind {}; 
    template<template<class...>class Src, template<class...>class Target, class...Ts> 
    struct rebind<Src<Ts...>, ztag<Target>, void_t<Target<Ts...>>>: 
    tag<Target<Ts...>> 
    {}; 
} 
template<class Src, class zDest> 
using rebind = typename details::rebind<Src,zDest>::type; 

namespace details { 
    template<template<class...>class Z, class, class...Ts> 
    struct can_apply : std::false_type {}; 
    template<template<class...>class Z, class...Ts> 
    struct can_apply<Z, void_t<Z<Ts...>>, Ts...> : std::true_type {}; 
} 
template<template<class...>class Z, class...Ts> 
using can_apply = typename details::can_apply<Z, void, Ts...>::type; 

template<class...>struct types{using type=types;}; 

namespace details { 
    template<class types, class...Us> 
    struct append; 
    template<class...Ts, class...Us> 
    struct append<types<Ts...>, Us...>: 
    types<Ts..., Us...> 
    {}; 
} 
template<class types, class...Us> 
using append = typename details::append<types, Us...>::type; 

template<class Src, template<class...>class Dest> 
using can_rebind_with_void = 
    can_apply< rebind, append< rebind<Src, ztag<types>>, void >, ztag<Dest> >; 

Live example

ztagは、タイプのみで作業するときにメタプログラミングがはるかに簡単であるためです。 ztagはテンプレートを取り、タイプに変換します。

zapplyは、それを適用します。いずれにせよ

namespace details { 
    template<class Z, class...Ts> 
    struct zapply {}; 
    template<template<class...>class Z, class...Ts> 
    struct zapply<ztag<Z>, Ts...>: 
    tag<Z<Ts...>> 
    {}; 
} 
template<class Z, class...Ts> 
using zapply = typename details::zapply<Z,Ts...>::type; 

は、すべてが1行のソリューションを除いて、一般的な次のとおりです。ここで

template<class Src, template<class...>class Dest> 
using can_rebind_with_void = 
    can_apply< rebind, append< rebind<Src, ztag<types>>, void >, ztag<Dest> >; 

我々はrebind< ???, ztag<Dest> >「我々が適用することができます」尋ねます。これはDest<???>です。

???は、Src<???>で始まり、そのタイプをtypes<???>に移動し、voidを追加します。したがってtypes<Ts..., void>Src<Ts...>になります。

rebindは、テンプレートargsを持つ型と別のテンプレートのztagを取り、最初の引数のテンプレートargsをztagのテンプレートに適用します。

can_applyは、暗黙のテンプレートアプリケーションが合法であるかどうかを尋ねます。私が一貫していれば、can_applyも最初の引数としてztagを取るでしょう。

ztagでこれを行うのは、テンプレートのメタプログラミングがよりスムーズだからです。

+2

オハイオ州神、あなたのスペースバーに何か間違っていますか? ;) – Oktalist

関連する問題