2016-04-25 6 views
4

ich eine Klasse foo mit Template-Parameter haben konstruiert werden Tuple und ich möchte ein variadische Argumente Konstruktor, um eine Membervariable m_elements vom Typ Tuple, zu initialisieren, wenn der Ausdruck m_elements{ static_cast<typename Tuple::value_type>(std::forward<Elements>(elements))... } ist definiert.Bedingtes einen Konstruktor ermöglichen, wenn eine Membervariable kann durch variadische Argumente

Wir können dies tun, auf folgende Weise:

template<class Tuple> 
struct foo 
{ 
    using value_type = typename Tuple::value_type; 

    template<class... Elements, class U = Tuple, 
     class = decltype(U{ static_cast<value_type>(std::declval<Elements>())... })> 
    foo(Elements&&... elements) 
     : m_elements{ static_cast<value_type>(std::forward<Elements>(elements))... } 
    {} 

    Tuple m_elements; 
}; 

Nun, ob dieser Konstruktor aktiviert ist oder nicht zu sehr auf einigen anderen Bedingungen abhängen. So würde ich brauche

etwas wie
template<class... Elements, class U = Tuple, 
    class = std::enable_if_t</* some other conditions depending on U */>, 
    class = decltype(U{ static_cast<value_type>(std::declval<Elements>())... })> 

schreiben Ich mag wäre meine erste Bedingung in Bezug auf std::is_constructible so prüfen, dass ich diese Prüfung in die enable_if bewegen konnte. Ist das möglich? Ich habe versucht, std::is_constructible_v<U, decltype(static_cast<value_type>(std::declval<Elements>()))...> zu verwenden, aber das scheint der vorherigen Überprüfung nicht zu entsprechen.

Zum Beispiel foo<bar<3>>{1, 2, 3}; mit

template<std::size_t N> 
struct bar 
{ 
    using value_type = double; 
    double data[N]; 
}; 

wird mit der vorherigen Überprüfung kompilieren, sondern ergibt sich ein Fehler mit dem neuen.

+0

Ironischerweise funktioniert 'foo' nicht, wenn' Tuple' = 'std :: tuple', weil' std :: tuple' kein 'value_type'-Attribut hat, also arbeiten Sie nicht * wirklich * mit Tupeln (im Sinne heterogener Kompilierzeitsammlungen). – Tim

+0

@Tim Es ist nicht so ironisch. Mein richtiger 'foo' ist in einer Mathebibliothek und ein Mathematiker definiert normalerweise einen Vektor als ein n-Tupel. – 0xbadf00d

+1

Ah, * das * Art von Tupel. Das macht viel mehr Sinn. – Tim

Antwort

1

Rostislav Wie erwähnt, wenn T kein Funktionstyp ist, ist std::is_constructible_v<T, Args>true iff der Variablendefinition T obj(std::declval<Args>()...); wohlgeformt. Das ist in bar<1> obj(0.); nicht der Fall, denn bar<1> hat keinen entsprechenden Konstruktor.

Im Gegensatz dazu ist bar1<1> obj{ 0. }; wohlgeformt. Mit dem vorgeschlagenen Detection Toolkit konnten wir

template<class T, typename... Arguments> 
using initializable_t = decltype(T{ std::declval<Arguments>()... }); 

template<class T, typename... Arguments> 
constexpr bool is_initializable_v = is_detected_v<initializable_t, T, Arguments...>; 

und ändern Sie den Scheck an

template<class... Elements, class U = Tuple, 
    class = std::enable_if_t<is_initializable_v<U, decltype(static_cast<value_type>(std::declval<Elements>()))...>>> 

verwenden denke ich, das ist besser lesbar als die Ebene decltype Ansatz.

0

Der Grund, dass die is_constructible Merkmal, dass anders ist verhält es gibt in der Tat keinen Konstruktor von bar die drei int Werte als Argument nehmen. In diesem speziellen Fall wird die Aggregatinitialisierung verwendet.

Aber man konnte den Test in ein enable_if nur setzen, indem ein einfacher Helfer struct mit:

#include <type_traits> 

template<typename T> 
struct test : std::true_type 
{ 
}; 

template<class Tuple> 
struct foo 
{ 
    using value_type = typename Tuple::value_type; 

    template<class... Elements, class U = Tuple, 
     class = std::enable_if_t< 
      test<decltype(U{ static_cast<value_type>(std::declval<Elements>())... })>::value> 
      /* && your other tests */ 
      > 
    foo(Elements&&... elements) 
     : m_elements{ static_cast<value_type>(std::forward<Elements>(elements))... } 
    {} 

    Tuple m_elements; 
}; 


template<std::size_t N> 
struct bar 
{ 
    using value_type = double; 
    double data[N]; 
}; 

int main() 
{ 
    foo<bar<3>>{1, 2, 3}; 
} 

LIVE

Alternativ können Sie die decltype Magie an einen separaten Ort schieben, indem ein Typ Merkmal zu schaffen Verwenden Sie eine der Antworten auf this Frage.

+0

Ihr Zustand 'enable_if' macht wenig Sinn. Wie würden Sie einen Konstruktor deaktivieren? –

+0

@PiotrSkotnicki Ich sehe nicht, was über das Deaktivieren eines Konstruktors überrascht. Sie unterliegen auch Funktionsüberlastung. – Quentin

+0

@Quentin dann zeigen einen Konstruktor, der ausgewählt werden kann, wenn diese Art von 'test' fehlschlägt, dh ein Konstruktor, der verwendet wird, wenn' U {Elemente ...} 'nicht verwendet werden kann –

Verwandte Themen