2013-02-11 3 views
14

Das folgende Programm, wenn mit GCC 4.7 und clang 3.2 kompiliert, erzeugt "1" als Ausgabe.Warum ist is_constructible Anspruch etwas ist konstruierbar, wenn es nicht ist?

#include <type_traits> 

struct foo { 
    template<typename T> 
    foo(T) { 
     static_assert(not std::is_same<int, T>(), "no ints please"); 
    } 
}; 

#include <iostream>  
int main() { 
    std::cout << std::is_constructible<foo, int>(); 
} 

Dies ist verwirrend. foo ist ganz eindeutig nicht konstruierbar von int! Wenn ich main der folgenden ändern, lehnen beide Compiler da es auf die statische Behauptung Versagen:

int main() { 
    foo(0); 
} 

Wie kommen beide Compiler sagen, es konstruierbar ist?

+0

Sie sollten lieber enable_if verwenden, um Ints von den möglichen Foos zu entfernen. – PlasmaHH

Antwort

21

Dies ist, was der Standard (§20.9.5/6), mit Hervorhebung von mir zu sagen hat:

der folgenden Funktionsprototyp Gegeben:

für eine Vorlage
template <class T> 
typename add_rvalue_reference<T>::type create(); 

das Prädikat Bedingung Spezialisierung is_constructible<T, Args...> müssen erfüllt werden, wenn und nur wenn die folgende Variablendefinition wohlgeformt für einige erfunden werden würde Variable t:

T t(create<Args>()...); 

[Hinweis: Dieser Marker wird nie als Funktion Erklärung interpretiert. -Ende note]

Zugriffskontrolle ist, als ob in einem Kontext, in keinem Zusammenhang mit T und jedem der Args durchgeführt. Nur die Gültigkeit des unmittelbaren Kontextes der Variableninitialisierung wird berücksichtigt. [] Hinweis: Die Auswertung der Initialisierung kann zu Nebeneffekten wie der Instanziierung von Klassenvorlagenspezialisierungen und Funktionsvorlagenspezialisierungen führen, die Generierung implizit definierter Funktionen und so weiter. Solche Seite Effekte sind nicht im "unmittelbaren Kontext" und kann dazu führen, dass das Programm schlecht gebildet wird. -Ende note]

Die Behauptung, nicht nur, wenn die Vorlage Konstruktor instanziiert wird. Wie in der Anmerkung dargelegt, steht diese Behauptung jedoch nicht im unmittelbaren Zusammenhang mit der betrachteten Variablendefinition und hat somit keinen Einfluss auf deren "Gültigkeit". Daher können die Compiler diese Definition als gültig zählen und somit behaupten, dass foo tatsächlich von int konstruierbar ist, selbst wenn tatsächlich versucht wird, einen foo aus einem int zu konstruieren, was zu einem schlecht ausgebildeten Programm führt.

Beachten Sie, dass die Compiler sind auch erlaubt, statt is_constructible Ausbeute falsch, nur das ursprüngliche Programm auf der Behauptung zurückweisen, obwohl keiner tut.

+10

Beachten Sie, dass es nur Sinn macht. 'std :: is_constructible <>' bestimmt, ob es einen Konstruktor gibt, der das Argument übernimmt, und nicht, dass der Konstruktor wohldefiniert ist. Beachten Sie, dass die Definition des Konstruktors (und damit des 'static_assert') für den Compiler bei der Verarbeitung des' is_constructible' nicht sichtbar sein muss. Wenn Sie das einschalten möchten, verwenden Sie SFINAE, um diesen Konstruktor für 'int' zu deaktivieren, oder fügen Sie alternativ' foo (int) = delete; 'hinzu, was den Konstruktor als nicht verfügbar markiert (nicht 100% sicher) es sollte funktionieren) –

5

foo2 ist Ihre foo. foo1 ist ein foo, das tut, was Sie Ihre foo tun möchten.

#include <type_traits> 
#include <utility> 

struct foo1 { 
    template<typename T,typename=typename std::enable_if< !std::is_same<int, T>::value >::type> 
    foo1(T) { 
    static_assert(not std::is_same<int, T>(), "no ints please"); 
    } 
}; 
struct foo2 { 
    template<typename T> 
    foo2(T) { 
    static_assert(not std::is_same<int, T>(), "no ints please"); 
    } 
}; 

#include <iostream>  
int main() { 
    std::cout << std::is_constructible<foo1, int>(); 
    std::cout << std::is_constructible<foo2, int>(); 
} 
+0

@ R.MartinhoFernandes wollte nur eine Antwort hier zu sein, falls jemand die Frage auf Google stellt. :) – Yakk

Verwandte Themen