2016-01-06 13 views
7

は、私はこのようなタイプがあります。 std::conditionalを使用しているソリューションでは、存在しない場合は何か別のものに置き換えることができますが、対応するタイプがテンプレートタイプに存在しない場合は、それがまったく存在しないことを確認する方法がわかりません。 Tが型エイリアスの1つを定義していない場合、wrapper<T>がインスタンス化されると上記のコードでエラーが発生します。条件付きタイプの別名定義

wrapperTから継承できないようにするため、wrapperはすべてTが行うことができないためできません。また、部分的な専門化を使用すると、何らかの指数関数的な爆発が起こり、すぐに維持不能になります。私はおそらくデフォルトのテンプレートパラメータにstd::enable_ifを注入するが、その後、ユーザーがwrapper<T>::foo<>wrapper<T>::bar<>代わりのwrapper<T>::foowrapper<T>::barなどを記述しなければならない...と私はそれをしたくない...テンプレート型の別名をfoobarを作ることができます。

対応するタイプのエイリアスがTに存在する場合にのみ、このようなタイプのエイリアスを定義する簡単で保守的な方法はありますか?

答えて

8

あなたはwrapperでそれらのすべてを継承し、その後、それが存在する場合にのみ型を持つcheck_foocheck_barcheck_baz特性を定義することができます。

template <typename T, typename=void> 
struct check_foo{}; 

template <typename T> 
struct check_foo<T, void_t<typename T::foo>> { 
    using foo = typename T::foo; 
}; 

// ditto for bar, baz, etc. 

template <typename T> 
struct wrapper : 
    check_foo<T>, 
    check_bar<T>, 
    check_baz<T> 
{ }; 

それはタイプごとに1つの余分構造体だが、それに確かに望ましいですあなたが言及した指数バージョン。私が知っている、

#define DEFINE_CHECKER(NAME) \ 
    template <typename T, typename=void> struct check_##NAME{}; \ 
    template <typename T> struct check_##NAME<T,void_t<typename T::NAME>> \ 
    { using NAME = typename T::NAME; }; 

DEFINE_CHECKER(foo) 
DEFINE_CHECKER(bar) 
DEFINE_CHECKER(baz) 

恐ろしいが、私はあなたが本当にwrapper<T>::barではなく、wrapper<T>::bar<>をしたい場合は、その価格を支払う必要があるかもしれないと思う:あなたが適切にあまのじゃくだったらあなたもそれマクロ作ることができます。マクロバージョンを使用する場合は、新しいタイプを追加すると、新しいDEFINE_CHECKER(newname)が追加され、check_newname<T>がラッパー継承リストに追加されます。悪化する可能性があります。

Live Demo

+1

これは私が念頭に置いた解決策であり、私はそれを試す準備ができました。実際にはそれほど美しいものではありませんが、他のソリューションよりもメンテナンスが容易です。ありがとう:) – Morwenn

+0

ダング。私は、これよりも良い解決策があることを期待していました。 – Justin

5

そのまま@TartanLlamaによってvoid_tを使用して答えは罰金であることに注意してください。しかし、C++では、標準ライブラリのヘルパー(is_detected_vなど)のほうがvoid_tへの呼び出しを行います。

#include <experimental/type_traits> 

// helpers to reduce boilerplate 
template<class Tag> 
struct empty_base {}; 

template<template<class> class Holder, template<class> class Op, class Arg> 
using inject_or_t = std::conditional_t 
< 
    std::experimental::is_detected_v<Op, Arg>, 
    Holder<Arg>, 
    empty_base<Op<Arg>> 
>; 

// add detector + holder for every conditional nested type 

template<class T> 
using foo_t = typename T::foo; 

template<class T> 
struct foo_holder { using foo = foo_t<T>; }; 

template<class T> 
using bar_t = typename T::bar; 

template<class T> 
struct bar_holder { using bar = bar_t<T>; }; 

template<class T> 
using baz_t = typename T::baz; 

template<class T> 
struct baz_holder { using baz = baz_t<T>; }; 

// wrapper is now simply: 

template<class T> 
struct wrapper 
: inject_or_t<foo_holder, foo_t, T> 
, inject_or_t<bar_holder, bar_t, T> 
, inject_or_t<baz_holder, baz_t, T> 
{}; 

struct Test 
{ 
    using foo = int; 
    using bar = int; 
    using baz = int; 
}; 

int main() 
{ 
    static_assert(!std::experimental::is_detected_v<foo_t, wrapper<int>>); 
    static_assert(!std::experimental::is_detected_v<bar_t, wrapper<int>>); 
    static_assert(!std::experimental::is_detected_v<baz_t, wrapper<int>>); 

    static_assert(std::experimental::is_detected_v<foo_t, wrapper<Test>>); 
    static_assert(std::experimental::is_detected_v<bar_t, wrapper<Test>>); 
    static_assert(std::experimental::is_detected_v<baz_t, wrapper<Test>>); 
} 

Live Example彼はのlibstdC++ 6.0 SVNトランクが(現在は!)SVNトランクはできません++ 3.9のlibc何かを行うことができ非常に稀な例の一つであることに注意してください。

これには、検出器エイリアスとホルダー構造体をそれぞれ注入型に追加する必要があり、マクロラッパーが不要です。

+0

本当にエレガントなソリューション!もちろん、適切なヘルパーを実装することによって、または[これ](http://coliru.stacked-crooked.com/a/cee230048c1a807c)のように、賢明にそれらを盗むことによって、C++ 14でこれを行うことができます。 – TartanLlama

+0

ああ、今それは興味深い解決策です:o – Morwenn

+0

@TemplateRexこれは@Morwennが求めているものではありませんが、そうですか? 'wrapper は' foo'、 'bar'と' baz'のメンバーエイリアスを継承するのではなく、 'int'から3回継承しようとします。 – TartanLlama

関連する問題