2016-04-20 9 views
1

I Züge haben Klassen über meinen Code bestreut, die die gleiche Grund Idiom folgen:Traits Klasse als Vorlage Template-Parameter

template<class Frame, typename = void> 
struct frame_traits 
{ 
    typedef void base_frame_type; 
}; 

template<class Frame> 
struct frame_traits<Frame, typename std::void_t< 
    typename Frame::base_frame_type>::type> 
{ 
    typedef typename Frame::base_frame_type   base_frame_type; 
}; 

und ich habe eine Reihe von Charakterzug Kontrolleure, die sie verwenden, die auch ein ähnliches Idiom folgen : aber

template <typename T> 
struct has_base_frame_type : std::integral_constant<bool, 
    !std::is_same<typename frame_traits<T>::base_frame_type, void>::value>::type {}; 

, stellt sich heraus, dass has_base_frame_type hat mehrere Konzepte in meinem Code nützlich werden, und ich möchte es weiter verallgemeinern, so dass ich die Züge Klasse als zusätzliche Parameter übergeben können:

template <typename T, template<typename> class Traits = frame_traits> 
struct has_base_frame_type : std::integral_constant<bool, 
    !std::is_same<typename Traits<T>::base_frame_type, void>::value>::type {}; 

Dies funktioniert jedoch nicht, seit templates with default arguments cannot be used as template template parameters.

Ich weiß, dass ich das Problem umgehen könnte, wenn ich immer eine Klasse Züge in der Vorlage Instanziierung verwenden (und ändern Sie die Eigenschaft checker, es zu akzeptieren), nämlich

has_base_frame_type<frame_traits<MyClass>>::value 

aber ich will nicht tun dass, weil es wäre zu vergessen und in einer Nicht-Merkmals-Klasse übergeben. Genau so hatte ich den Code ursprünglich geschrieben, bis ich das Merkmal zu oft vergessen und neu strukturiert habe.

Gibt es eine Möglichkeit, dass ich mein Trait-Klassen-Idiom ändern kann, um das Template-Template-Parameter-Problem zu umgehen?

+0

So etwas wie das [Erkennungs-Idiom] (http://coliru.stacked-crooked.com/a/779edd372df54f26)? –

+0

@Piotr Ich bin nicht gut darin, Template-Metacode zu lesen, aber ich bin mir nicht sicher, ob deine Alias-Eigenschaften die gleichen Eigenschaften wie meine Traits-Klasse haben, nämlich der Typedef wird immer definiert, egal was die Template-Eingabe ist, aber er wird ungültig sein wenn der typedef nicht durch 'T' definiert wurde. Außerdem ist es für mich nicht offensichtlich, dass es auf Merkmalsklassen skaliert, die mehrere Typdefinitionen definieren. –

+0

Sie meinen [diese] (http://coliru.stacked-crooked.com/a/0108aa409e8e95fe)? Was meinst du mit "es skaliert nicht"? Warum brauchst du einen Dummy 'void' Typedef, wenn es nur ein Merkmal ist? –

Antwort

2

Framework:

#include <type_traits> 

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

template <typename AlwaysVoid, template <typename...> class Operation, typename... Args> 
struct detect_impl : std::false_type {}; 

template <template <typename...> class Operation, typename... Args> 
struct detect_impl<void_t<Operation<Args...>>, Operation, Args...> : std::true_type {}; 

template <template <typename...> class Operation, typename... Args> 
using detect = detect_impl<void, Operation, Args...>; 

Detektoren:

template <class Frame> 
using frame_traits = typename Frame::base_frame_type; 

template <class Frame> 
using other_frame_traits = typename Frame::other_frame_type; 

Trait mit einem Standard-Detektor:

template <typename T, template <typename...> class Traits = frame_traits> 
using has_frame_type = detect<Traits, T>; 

Test:

struct A 
{ 
    using base_frame_type = void; 
}; 

struct B 
{ 
    using other_frame_type = void; 
}; 

int main() 
{ 
    static_assert(has_frame_type<A>{}, "!"); // default 
    static_assert(!has_frame_type<B>{}, "!"); // default 

    static_assert(!has_frame_type<A, other_frame_traits>{}, "!"); // non-default 
    static_assert(has_frame_type<B, other_frame_traits>{}, "!"); // non-default 
} 

DEMO