2012-10-03 8 views
5

Durante il processo di perfezionamento dello standard C++ 11, sembra che is_trivially_destructible sia stato considerato un nome migliore/più consistente di has_trivial_destructor.Scrittura del codice che funziona quando "has_trivial_destructor" è definito, invece di "is_trivially_destructible"

Questo è uno sviluppo relativamente recente, come il mio g ++ 4.7.1 utilizza ancora il vecchio nome, e che è stato fissato per essere compatibile con lo standard a partire dal 4.8:

http://gcc.gnu.org/bugzilla/show_bug.cgi?id=52702

Sono stato pigramente utilizzando un #if che favorisce il compilatore sono su:

#if TRIVIAL_DESTRUCTOR_TYPE_TRAIT_MATCHES_STANDARD 
template<class T> 
using is_trivially_destructible = std::is_trivially_destructible<T>; 
#else 
template<class T> 
using is_trivially_destructible = std::has_trivial_destructor<T>; 
#endif 

... ma ora sto cercando di condividere la sorgente con 4.8 gli utenti e gli altri compilatori inseguono lo standard. C'è un trucco migliore per rendere il rilevamento della situazione più "automatico" e non richiedere un #define?

+2

L'utilizzo di una macro predefinita come "__GNUC_MINOR__" potrebbe renderlo un po 'più semplice. –

+0

@VaughnCato Yup, è sicuramente un passo avanti, ma sembra che MSVC e altri stiano diventando conformi alle loro condizioni. Potrebbe consentire a un albero #if di evolvere per comprendere il set di utenti, ma sperava che fosse analogo a questo: http://stackoverflow.com/questions/257288/is-it-possible-to-write-ac-template-to -check-for-a-functions-esistenza – HostileFork

risposta

9

questo funziona per me con GCC 4.7 e 4.8, in modo corretto dicendomi che il vecchio o nuovo tratto è previsto:

#include <type_traits> 

namespace std 
{ 
    template<typename> struct has_trivial_destructor; 
    template<typename> struct is_trivially_destructible; 
} 

template<typename T> 
    class have_cxx11_trait_helper 
    { 
    template<typename T2, bool = std::is_trivially_destructible<T2>::type::value> 
     static std::true_type test(int); 

    template<typename T2, bool = std::has_trivial_destructor<T2>::type::value> 
     static std::false_type test(...); 

    public: 
    typedef decltype(test<T>(0)) type; 
    }; 

template<typename T> 
    struct have_cxx11_trait : have_cxx11_trait_helper<T>::type 
    { }; 

int main() 
{ 
    static_assert(have_cxx11_trait<int>::value, "new trait"); 
} 

N.B. Dichiaro (ma non definisco) entrambi i tratti perché la libreria standard (probabilmente) non dichiarerà entrambi e se il nome non è dichiarato, non è possibile fare riferimento a std::is_trivially_destructible. Quindi li dichiaro entrambi, ma solo quello definito dalla biblioteca sarà utilizzabile. L'aggiunta di dichiarazioni allo spazio dei nomi std è tecnicamente indefinita, quindi usala a tuo rischio (non è probabile che cancelli il tuo disco rigido in questo caso però)

Sfortunatamente un compilatore più vecchio che non fornisce il nuovo tratto potrebbe non in grado di gestire il codice sia - non ho controllato se funziona con GCC 4.6

Ora è possibile definire il proprio tratto portatile:

template<typename T> 
    using is_trivially_destructible 
    = typename std::conditional<have_cxx11_trait<T>::value, 
           std::is_trivially_destructible<T>, 
           std::has_trivial_destructor<T>>::type; 

la semantica di has_trivial_destructor non sono le stesse di il nuovo tratto, ma è un'approssimazione ragionevole per i compilatori più vecchi che non lo fanno il nuovo tratto.

In alternativa, è possibile utilizzare il polimorfismo statico per ottenere codice diverso a seconda del tipo di tratto disponibile, ad es. specializzandosi modelli o da sovraccarichi e tag di dispacciamento, in questo modo:

template<typename T> 
    void foo_helper(const T&, std::true_type) 
    { 
    // code that uses std::is_trivially_destructible 
    } 

template<typename T> 
    void foo_helper(const T&, std::false_type) 
    { 
    // different code using std::has_trivial_destructor 
    } 

template<typename T> 
    void foo(const T& t) 
    { 
    // do common stuff 

    // stuff that depends on trait 
    foo_helper(t, has_cxx11_trait<T>{}); 

    // more common stuff 
    } 

Non sono le macro sono stati lesi nella realizzazione di questa risposta.

+0

Ahh, non ti ho visto pubblicare una risposta. :(Dopo aver letto questo, sembra essere la via più ovvia, dang – Xeo

+0

Sì, è il genere di cose che stavo cercando! È facile spiegare la differenza semantica tra "has_trivial_destructor" e "is_trivially_destructible" o dovrebbe essere una nuova domanda? :-) – HostileFork

+1

In realtà per quei tratti potrebbe non esserci alcuna differenza significativa. Per alcuni dei tratti di _constructible_ ci sono sottili differenze tra le vecchie e le nuove forme, vedi http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2010/n3142.html per ulteriori –

4

Ecco un molto hacker e ufficialmente UB frammento in grado di verificare se lo spazio dei nomi std sport il nome has_trivial_destructor e ha un trivially_destructible alias tratto raccogliere il tratto diritto di verificare (is_trivially_destructible nel caso has_trivial_destructor non è disponibile).

#include <type_traits> 

template<class> 
struct has_trivial_destructor{ using test_fail = int; }; 

template<class> 
struct is_trivially_destructible{ using test_fail = int; }; 

// very hackish and officially UB 
namespace std{ 
    template<class T> 
    struct inherit_htd : has_trivial_destructor<T>{}; 
    template<class T> 
    struct inherit_itd : is_trivially_destructible<T>{}; 
} 

namespace check_htd{ 
    template<class T> 
    struct sfinae_false : ::std::false_type{}; 

    template<class T> 
    auto test(int) -> sfinae_false<typename ::std::inherit_htd<T>::test_fail>; 
    template<class> 
    auto test(...) -> ::std::true_type; 

    struct htd_available : decltype(test<int>(0)){}; 
} 

template<class T> 
using Apply = typename T::type; 

template<class C, class T, class F> 
using If = Apply<std::conditional<C::value,T,F>>; 

template<class T> 
using trivially_destructible = If<check_htd::htd_available, std::inherit_htd<T>, std::inherit_itd<T>>; 

Live example.

+0

Hackish che funziona, funziona! Ma come soluzione ideale non metterebbe nulla di nuovo nello std :: namespace ... tuttavia sia la tua soluzione sia quella di @JonathanWakely lo hanno fatto ... c'è un argomento fondamentale per capire perché è necessario per raggiungere questo obiettivo? – HostileFork

+0

Poiché è mal formato per fare riferimento a un nome qualificato 'A :: foo' se' foo' non è già stato dichiarato in tale ambito. SFINAE non si applica perché 'A' non è un modello, quindi non vi è alcuna sostituzione degli argomenti del modello in atto. –

1

ho problemi simili, e in precedenza controllato per GCC versione macro (purtroppo non c'è modo per verificare il corretto libstd ++ versione, solo un codice data è disponibile). Previous Answer by Jonathan Wakely è una buona soluzione, ma non riesce con libC++ e probabilmente altre librerie che definiscono i modelli in uno spazio dei nomi inline versione o mappano tale spazio dei nomi in std tramite l'uso. In questo modo i prototipi non si adatteranno.

Per rendere il codice di Jonathan Wakely idoneo, è necessario verificare la presenza di libC++ e definire lo spazio dei nomi corretto.

#include <type_traits> 


#ifdef _LIBCPP_BEGIN_NAMESPACE_STD 
_LIBCPP_BEGIN_NAMESPACE_STD 
#else 
namespace std { 
#endif 
    template<typename> struct has_trivial_destructor; 
    template<typename> struct is_trivially_destructible; 
// All unimplemented in gcc 4.9 
    template<typename, typename...> struct is_trivially_constructible; 
    template<typename> struct is_trivially_default_constructible; 
    template<typename> struct is_trivially_copy_constructible; 
    template<typename> struct is_trivially_move_constructible; 
    template<typename> struct is_trivially_assignable; 
    template<typename> struct is_trivially_copy_assignable; 
    template<typename> struct is_trivially_move_assignable; 
    template<typename> struct is_trivially_copyable; 
#ifdef _LIBCPP_BEGIN_NAMESPACE_STD 
_LIBCPP_END_NAMESPACE_STD 
#else 
} // namespace std 
#endif 

template<typename T> 
    class have_cxx11_trait_helper 
    { 
    template<typename T2, bool = std::is_trivially_destructible<T2>::type::value> 
     static std::true_type test(int); 

    template<typename T2, bool = std::has_trivial_destructor<T2>::type::value> 
     static std::false_type test(...); 

    public: 
    typedef decltype(test<T>(0)) type; 
    }; 

template<typename T> 
    struct have_cxx11_trait : have_cxx11_trait_helper<T>::type 
    { }; 

int main() 
{ 
static_assert(have_cxx11_trait<int>::value, "new trait"); 
}