2016-04-04 5 views
2

Ist es möglich zu testen, ob einige Typen über SFINAE an einen Vorlagenparameter gebunden werden können?Verwenden Sie SFINAE, um zu prüfen, ob Typen an Vorlagenvorlagenparameter gebunden werden können

Ich denke, was ich versuche, am besten zu tun ist, mit dem folgenden Beispielcode erläutert:

#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; 
} 

Zuerst habe ich eine temporäre Art machen tmp aus dem ich die Template-Parameter int zu binden es in eine andere Klasse nehmen wollen Vorlage. Mit der TestValidBind<template template type>::toTypesOf<typename> möchte ich testen, ob es möglich ist, den Parameter des angegebenen Typs an die template template parameterund zu binden und einen zusätzlichen Typ anzufügen (float in dem Beispiel).

Was ich will, ist, dass TestValidBind<TwoParamStruct>::toTypesOf<tmp>::type ist true_type während TestValidBind<OneParamStruct>::toTypesOf<tmp>::typefalse_type ist.


Das Codebeispiel, wie es mit g++ -std=c++11 (5.3.1) mit dem folgenden Fehler nicht kompilieren ist:

../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

und Berichte false_type (was falsch ist), wenn die OneParamStruct Linie entfernt wird.

Mit clang++ -std=c++11 (3.8.0) kompiliert der Code aber Berichte false_type in beiden Fällen.

Ist so etwas überhaupt möglich?


Edit: Changed die zusätzliche Art von void zu float betonen, dass ich, wenn die zusätzliche Art überprüfen möchten möglich ist.

Antwort

2

Der Trick void_t erfordert, dass wir einem der Parameter der primären Vorlage einen Standardtyp void geben. Die primäre Vorlage (toTypesOf) musste nicht variabel sein.

Es ist mehr idiomatische für ein bool Typ Merkmal von false_type oder true_type statt erben eine verschachtelte type haben. Es ist nicht erforderlich, TemporaryBindObject eine verschachtelte type zu haben.

TestValidBind sollte wie folgt aussehen:

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: Wie Sie sagen, bedeutet dies nicht mit g ++ arbeiten, und ich weiß nicht, warum das so ist. Aber wir können es vereinfachen und sehen, ob das hilft. Wir brauchen die umschließende Struktur nicht wirklich TestValidBind. Die toTypesOf Vorlage kann im Namensraum Umfang, wenn wir die Dest Parameter hinein Transplantation:

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 
{}; 

Dies funktioniert in g ++.

DEMO

Wir gehen weiter und machen es ein bisschen einfacher zu benutzen, wenn man so will, indem sie alles in einem detail Namensraum setzen und es in einem Alias-Vorlage Verpackung:

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; 

Jetzt sind wir don 't brauchen die using tmp, und wir haben eine allgemeinere Lösung, in der Sie leicht den Typ ändern können, den Sie als zusätzlichen Typ verwenden möchten.

DEMO

+0

Danke für Ihre gültigen Korrekturen. Eigentlich beantwortet es meine Frage nicht genau. Ich habe nach dem Gegenteil gefragt, das immer noch nicht funktioniert (wahrscheinlich war mein Beispiel nicht gut darauf zugeschnitten, den Punkt hervorzuheben). Was ich versuchen wollte, war einen zusätzlichen Typ hinzuzufügen (den ich als "ungültig" auswählte). Ich werde es in etwas anderes ändern, um diesen Punkt hervorzuheben. – havogt

+0

Ich war ein bisschen zu schnell mit meinem Kommentar: Mein Beispiel (mit einem Typ zu viele) mit Ihren Korrekturen funktioniert jetzt mit clang, schlägt aber immer noch mit g ++ 5 fehl. Ihr Beispiel funktioniert in beiden Compilern. – havogt

+0

Ich werde meine Frage an Ihre Antwort anpassen (weil Sie tatsächlich einen dummen Fehler behoben haben) und morgen eine neue Frage stellen. Aber lassen Sie mich beachten, dass ich meine Frage mit dem Schnitt nicht geändert habe! – havogt

1

Hier ist, wie ich es tun würde:

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.

Die ztag ist, weil Metaprogrammierung viel einfacher ist, wenn nur mit Typen gearbeitet wird. ztag nimmt eine Vorlage und verwandelt sie in einen Typ.

zapply dann gilt:

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; 

Auf jeden Fall alles außer dem einzeiligen Lösung generisch ist:

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

Hier stellen wir "können wir anwenden" rebind< ???, ztag<Dest> >. Dies tut eine Dest<???>.

??? beginnt mit Src<???>, verschiebt seine Typen auf types<???>, fügt eine void an. So erhalten wir types<Ts..., void> von Src<Ts...>.

rebind nimmt einen Typ mit Vorlage Args, und eine ztag einer anderen Vorlage, und wendet die erste Argumente Vorlage Args auf die Vorlage innerhalb der ztag.

can_apply fragt, ob die implizite Vorlagenanwendung zulässig ist. Wenn ich konsistenter wäre, würde can_apply auch ein ztag als sein erstes Argument nehmen.

Der Grund, warum ich das alles mit ztag mache, ist, weil Vorlage Metaprogrammierung ist glatter ist alles Typen.

+2

Oh Gott, stimmt etwas nicht mit deiner Leertaste? ;) – Oktalist

Verwandte Themen