2016-04-02 12 views
2

私が助けが必要なのは、以下のあいまいさを解決することです。しかし、あいまいさが解消されれば、私はまだ8つのスペシャライゼーションを実装するためのより簡潔でエレガントな方法があるかどうかを知る必要があります。テンプレートの特殊化で複数のvoid_t呼び出し

#include <iostream> 

template <typename> 
using void_t = void; 

template <typename T, typename U, typename = void, typename = void, typename = void> 
struct Foo { 
    static void call() {std::cout << "Case 1\n";} 
}; 

template <typename T, typename U> 
struct Foo<T, U, 
     void_t<decltype(std::declval<T>().foo(int()))>, 
     void_t<decltype(std::declval<U>().bar(bool(), char()))>, 
     void_t<decltype(execute(std::declval<const T&>(), std::declval<const U&>()))>> { 
    static void call() {std::cout << "Case 2\n";} 
}; 

template <typename T, typename U> 
struct Foo<T, U, 
     void_t<decltype(std::declval<T>().foo(int()))>, 
     void, void> { 
    static void call() {std::cout << "Case 3\n";} 
}; 
// etc... for the remaining 5 specializations. 

struct Thing { 
    void foo(int) {} 
}; 

struct Uber { 
    int bar(bool, char) {return 2;} 
}; 

void execute (const Thing&, const Uber&) {} 

int main() { 
    Foo<Thing, int>::call(); // Case 3 
// Foo<Thing, Uber>::call(); // Ambiguous. Want this to be "Case 2" instead of "Case 3". 
} 

だからまずFoo<Thing, Uber>::call();があいまいであることを知る必要があります。 3つのvoid_tがすべて満たされているため、ケース2はケース3よりも特殊化されていませんか?また、私は3つのvoid_tの2x2x2の可能性を満たすために、さらに5つの専門化を行うつもりです。 n void_tが使用されている場合、2^nの特殊化を扱う最もエレガントな方法は何ですか?アナロジーとして

は、

#include <iostream> 
#include <type_traits> 

template <bool B> using bool_constant = std::integral_constant<bool, B>; 

template <std::size_t N> 
struct is_even : bool_constant<N % 2 == 0> {}; 

template <std::size_t N, std::size_t A> 
struct add_to_odd : bool_constant<(N + A) % 2 == 1> {}; 

template <std::size_t N, std::size_t A, std::size_t B> 
struct is_one_of_these : bool_constant<N == A || N == B> {}; 

template <std::size_t N, std::size_t A, std::size_t B, typename = void, typename = void, typename = void> 
struct Foo { 
    static void call() {std::cout << "Case 1\n";} 
}; 

template <std::size_t N, std::size_t A, std::size_t B> 
struct Foo<N,A,B, 
     std::enable_if_t<is_even<N>::value>, 
     std::enable_if_t<!add_to_odd<N,A>::value>, 
     std::enable_if_t<!is_one_of_these<N,A,B>::value>> { 
    static void call() {std::cout << "Case 2\n";} 
}; 

// etc... for the other combinations of the 3 enable_if conditions being true/false. 

int main() { 
    Foo<1,2,3>::call(); 
    Foo<8,2,3>::call(); 
} 

のような3つのstd::enable_if_tコールを処理した場合のために、私は8つのテンプレート特殊はより、簡潔、保守、および読みやすくするために

#include <iostream> 
#include <type_traits> 

template <bool B> using bool_constant = std::integral_constant<bool, B>; 

template <std::size_t N> 
struct is_even : bool_constant<N % 2 == 0> {}; 

template <std::size_t N, std::size_t A> 
struct add_to_odd : bool_constant<(N + A) % 2 == 1> {}; 

template <std::size_t N, std::size_t A, std::size_t B> 
struct is_one_of_these : bool_constant<N == A || N == B> {}; 

template <std::size_t N, std::size_t A, std::size_t B, bool, bool, bool> 
struct FooHelper { 
    static void call() {std::cout << "Case 1\n";} 
}; 

template <std::size_t N, std::size_t A, std::size_t B> 
struct FooHelper<N, A, B, true, false, false> { 
    static void call() {std::cout << "Case 2\n";} 
}; 

template <std::size_t N, std::size_t A, std::size_t B> 
struct FooHelper<N, A, B, true, true, false> { 
    static void call() {std::cout << "Case 3\n";} 
}; 

template <std::size_t N, std::size_t A, std::size_t B> 
struct FooHelper<N, A, B, true, true, true> { 
    static void call() {std::cout << "Case 4\n";} 
}; 

template <std::size_t N, std::size_t A, std::size_t B> 
struct FooHelper<N, A, B, false, true, true> { 
    static void call() {std::cout << "Case 5\n";} 
}; 

template <std::size_t N, std::size_t A, std::size_t B> 
struct FooHelper<N, A, B, false, false, true> { 
    static void call() {std::cout << "Case 6\n";} 
}; 

template <std::size_t N, std::size_t A, std::size_t B> 
struct FooHelper<N, A, B, true, false, true> { 
    static void call() {std::cout << "Case 7\n";} 
}; 

template <std::size_t N, std::size_t A, std::size_t B> 
struct FooHelper<N, A, B, false, true, false> { 
    static void call() {std::cout << "Case 8\n";} 
}; 

template <std::size_t N, std::size_t A, std::size_t B> 
struct Foo : FooHelper<N, A, B, is_even<N>::value, add_to_odd<N,A>::value, is_one_of_these<N,A,B>::value> {}; 

int main() { 
    Foo<1,2,3>::call(); 
    Foo<8,2,3>::call(); 
    // etc... 
} 

を考え出しました。しかし、n void_tの類推はそれほど明白ではありません(上記のあいまいさは本当に役に立ちません)。

答えて

1

あなたのケースでは、ケース2とケース3の両方が同じように特殊化されていますが、別様に書いています。 void_t<decltype(std::declval<U>().bar(bool(), char()))>voidです。

フラグを追加して、使用する特殊化を指定できます。代わりに、変換順序のランク付けであいまいさを解決できます。

#include <iostream> 

template <typename> 
using void_t = void; 

template <int P> 
struct Rank : Rank<P - 1> {}; 

template <> 
struct Rank<0> : std::integral_constant<int, 0> {}; 

// Helper functions 
template <typename T> 
static auto has_foo_impl(int) 
    -> decltype(std::declval<T>().foo(int()), std::true_type()); 
template <typename T> 
static auto has_foo_impl(long) -> std::false_type; 

template <typename T> 
constexpr bool has_foo() { 
    return std::is_same<decltype(has_foo_impl<T>(0)), std::true_type>::value; 
}; 

template <typename T> 
static auto has_bar_impl(int) 
    -> decltype(std::declval<T>().bar(bool(), char()), std::true_type()); 
template <typename T> 
static auto has_bar_impl(long) -> std::false_type; 

template <typename T> 
constexpr bool has_bar() { 
    return std::is_same<decltype(has_bar_impl<T>(0)), std::true_type>::value; 
}; 

template <typename T, typename U> 
static auto has_execute_impl(int) 
    -> decltype(execute(std::declval<T&>(), std::declval<U&>()), 
       std::true_type()); 
template <typename T, typename U> 
static auto has_execute_impl(long) -> std::false_type; 

template <typename T, typename U> 
constexpr bool has_execute() { 
    return std::is_same<decltype(has_execute_impl<T, U>(0)), 
         std::true_type>::value; 
}; 

// Call overloads 
template <typename T, typename U> 
static void call_impl(Rank<0>) { 
    std::cout << "Case 1\n"; 
} 

template <typename T, typename U> 
static auto call_impl(Rank<5>) 
    -> std::enable_if_t<has_foo<T>() && has_bar<U>() && has_execute<T, U>()> { 
    std::cout << "Case 2\n"; 
} 

template <typename T, typename U> 
static auto call_impl(Rank<4>) -> std::enable_if_t<has_foo<T>()> { 
    std::cout << "Case 3\n"; 
} 

template <typename T, typename U> 
struct Foo { 
    static void call() { call_impl<T, U>(Rank<10>()); } 
}; 

struct Thing { 
    void foo(int) {} 
}; 

struct Uber { 
    int bar(bool, char) { return 2; } 
}; 

void execute(const Thing&, const Uber&) {} 

int main() { 
    Foo<Thing, int>::call(); // Case 3 
    Foo<Thing, Uber>::call(); // Ambiguous. Want this to be "Case 2" instead of 
          // "Case 3". 
} 
関連する問題