2013-02-11 5 views
14

Il seguente programma, quando compilato con GCC 4.7 e clang 3.2, produce "1" come output.Perché is_constructible afferma che qualcosa è costruibile quando non lo è?

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

Questo è confuso. foo non è chiaramente costruibile da int! Se cambio main al seguente, entrambi i compilatori lo rifiutano a causa della mancanza di affermazione statica:

int main() { 
    foo(0); 
} 

Come mai entrambi i compilatori dicono che è costruibile?

+0

Si consiglia di utilizzare enable_if per rimuovere gli inolt dai possibili foos. – PlasmaHH

risposta

21

Questo è ciò che la norma ha da dire (§20.9.5/6), con la mia attenzione:

data funzione il seguente prototipo:

template <class T> 
typename add_rvalue_reference<T>::type create(); 

la condizione del predicato per un modello specializzazione is_constructible<T, Args...> deve essere soddisfatta se e solo se la definizione di variabile seguente sarà ben formata per qualche variabile t

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

[Nota: Questi gettoni sono mai interpretati come un dichiarazione di funzione. -end nota]

controllo di accesso viene eseguita come se in un contesto estraneo a T e una delle Args. Viene considerata solo la validità del contesto immediato dell'inizializzazione della variabile . [Nota: La valutazione del inizializzazione può provocare effetti collaterali quali l'istanziazione di template specializzazioni classe e specializzazioni modello di funzione, la generazione di funzioni implicitamente definite, e così via. Tali effetti laterali non sono nel "contesto immediato" e possono provocare l'errato formato del programma . -end nota]

L'asserzione non riesce solo quando il costruttore del modello viene creata un'istanza. Tuttavia, come chiarito nella nota, tale asserzione non è nel contesto immediato della definizione della variabile che è considerata, e quindi non influisce sulla sua "validità". Pertanto, i compilatori possono considerare valida la definizione e quindi dichiarare che foo è effettivamente costruibile da int, anche se in realtà si sta tentando di costruire un foo da un int risultato in un programma mal formato.

Si noti che i compilatori sono autorizzati a, invece di avere is_constructible resa false, rifiutare semplicemente il programma originale basato sull'asserzione, anche se nessuno dei due lo fa.

+10

Nota che ha senso solo. 'std :: is_constructible <>' determina se esiste un costruttore che accetta l'argomento, non che il costruttore sia ben definito. Considera che la definizione del costruttore (e quindi il 'static_assert') non deve essere visibile al compilatore quando si elabora' is_constructible'. Se vuoi che si avvii, usa SFINAE per disattivare quel costruttore per 'int', o in alternativa aggiungi' foo (int) = delete; 'che segnerà il costruttore come non disponibile (non sicuro al 100% di quest'ultimo approccio, ma dovrebbe funzionare) –

5

foo2 è il tuo foo. foo1 è un foo che fa ciò che vuoi fare con il tuo foo.

#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 voleva solo una risposta per essere qui nel caso qualcuno colpisse la domanda su google. :) – Yakk