2016-07-23 24 views
2

Meine Frage in C zu wählen sind eine Erweiterung dieser Frage: How to use sfinae for selecting constructors?Wie SFINAE verwendet Konstruktor von mehreren Optionen ++ 11

In der vorherige Frage, die Fragesteller nur einen einzigen Konstruktor selektiv aktivieren wollte. Ich möchte das Verhalten des Konstruktors abhängig davon ändern, ob der Klassenschabloneparametertyp standardmäßig-constructible ist - der beste Weg, den ich dazu finden kann, ist zwei Konstruktoren mit der gleichen Verwendung zu haben, so dass genau einer aktiviert ist für jede Instantiierung. Mein Fall ist auch anders, weil keine Version des Konstruktors eine Template-Funktion wäre, wenn ich nicht versuchen würde, selektiv mit enable_if zu aktivieren (während in der verknüpften Frage beide Versionen des Konstruktors auf int otherN gestempelt sind).

Die Kommentare in der akzeptierten Antwort auf die obige Frage führten mich zu this site, was mich dazu gebracht, das folgende minimal Beispiel zu erstellen:

#include <iostream> 
#include <type_traits> 

namespace detail { 
    enum class enabler {}; 
    enum class disabler {}; 
} 

template <typename Condition> 
using EnableIf = typename std::enable_if<Condition::value, detail::enabler>::type; 

template <typename Condition> 
using DisableIf = typename std::enable_if<!Condition::value, detail::disabler>::type; 

template<typename T> 
struct A { 

    T data; 

    // Valid if T is default-construtible; SFINAE otherwise 
    template<EnableIf<std::is_default_constructible<T>>...> 
    A() { std::cout << "Data defaulted" << std::endl; } 


    // Valid if T is *not* default-constructible; SFINAE otherwise 
    template<DisableIf<std::is_default_constructible<T>>...> 
    A() : data(0) { std::cout << "Data zeroed" << std::endl; } 
}; 

// struct which is not default-constructible 
struct B { 
    B() = delete; 
    B(int) {} 
}; 

int main() 
{ 
    A<int> x; // int is default-constructible 
    A<B> y; // class B is not default-constructible 

    return 0; 
} 

Das kann ich kompilieren (mit -std=c++11), wenn ich den ersten Kommentar aus Konstruktor und die Deklaration von x oder dem zweiten Konstruktor und die Deklaration von y. Ich möchte beides nicht tun, aber wenn ich versuche, dass der Compiler beschwert, dass es keinen type in std::enable_if<false, > genannten Typ gibt.

Die Antworten auf this Frage nehmen einen anderen Ansatz zu einem ähnlichen Problem, aber ich verstehe die Faktoren im Spiel nicht gut genug, um in der Lage zu sein, die Ansätze in etwas zu kombinieren, das funktioniert.

+0

[fast statisch-if] (https://rmf.io/cxx11/almost-static-if) Link muss aktualisiert werden. (kein abschließender Schrägstrich) – tukra

+0

Korrigiert, danke – user1476176

Antwort

4

Nicht ideal, aber das wird die Arbeit getan:

#include <iostream> 
#include <type_traits> 

template<typename T> 
struct A { 

    T data; 

    A() : A((std::is_default_constructible<T> *)nullptr) {} 

private: 
    A(std::true_type *) { std::cout << "Data defaulted" << std::endl; } 

    A(std::false_type *) : data(0) { std::cout << "Data zeroed" << std::endl; } 
}; 

// struct which is not default-constructible 
struct B { 
    B() = delete; 
    B(int) {} 
}; 

int main() 
{ 
    A<int> x; // int is default-constructible 
    A<B> y; // class B is not default-constructible 

    return 0; 
} 
+1

Das Entfernen der Zeiger und das Übergeben von 'std :: is_default_constructible :: type' macht stattdessen das Lesen von imo ... sonst +1. – davidhigh

0

Sie könnten auch zwei alternative Klassenimplementierungen erstellen, die auf dem T abhängig und seine Standard konstruierbar Fähigkeit:

#include <type_traits> 
#include <iostream> 

template <class T, class = void> 
struct A_parent; 

template <class T> 
struct A_parent<T, typename std::enable_if<std::is_default_constructible<T>::value>::type> { 
    T data; 
    A_parent() { std::cout << "Default constructable" << std::endl; } 
}; 

template <class T> 
struct A_parent<T, typename std::enable_if<!std::is_default_constructible<T>::value>::type> { 
    T data; 
    A_parent(): data(0) { std::cout << "Not default constructable" << std::endl; } 
}; 

template <class T> 
struct A: A_parent<T> { 
    /* further implementation */ 
}; 

struct B { 
    B() = delete; 
    B(int) {} 
}; 

int main() { 
    A<int> a1; 
    A<B> a2; 
} 

Ausgang:

Default constructable 
Not default constructable 
2

Abgesehen von @ Sams Tag Versandlösung können Sieverwenden 10 auf den Konstrukteuren, aber Sie haben sich Folgendes bewusst sein:

  1. der Template-Parameter für die std::is_default_constructible verwendet nicht T sein können, stattdessen müssen Sie eine „neue“ Template-Parameter (die T vorbelegt werden kann) . Sehen Sie diese SO Frage/Antwort für Details: std::enable_if to conditionally compile a member function

  2. Zitiert aus cppreference.com:

Ein häufigen Fehler ist zwei Funktionsschablonen zu deklarieren, die nur in ihrer Standardvorlage Argumenten unterscheiden. Dies ist illegal, da die Standardschablonenargumente nicht Teil der Signatur der Funktionsschablone sind und zwei verschiedene Funktionsschablonen mit derselben Signatur deklarieren, ist ungültig.

Dies führt zu dem folgenden Code:

#include <iostream> 
#include <type_traits> 

template<typename T> 
struct A { 

    T data; 

    // Valid if T is default-constructible; SFINAE otherwise 
    template<typename X = T, typename SFINAE = typename std::enable_if<std::is_default_constructible<X>::value>::type, typename P = SFINAE> 
    A() { std::cout << "Data defaulted" << std::endl; } 


    // Valid if T is *not* default-constructible; SFINAE otherwise 
    template<typename X = T, typename = typename std::enable_if<!std::is_default_constructible<X>::value>::type> 
    A() : data(0) { std::cout << "Data zeroed" << std::endl; } 
}; 

// struct which is not default-constructible 
struct B { 
    B() = delete; 
    B(int) {} 
}; 

int main() 
{ 
    A<int> x; // int is default-constructible 
    A<B> y; // class B is not default-constructible 

    return 0; 
} 

live example

+0

Ich bemerke, dass Sie '-std = C++ 14' in Ihrem Live-Beispiel verwendet haben, und auch, dass dies nicht kompiliert wird, wenn ich' -std = C++ 11' verwende. Welche Veränderung macht das in 14, aber nicht in 11 möglich? – user1476176

+0

@ user1476176 Ich aktualisierte den Code, um mit C++ 11 kompatibel zu sein; Die einzige Änderung war die Verwendung von 'std :: enable_if' anstelle von' std :: enable_if_t' –