Domanda

Dopo aver risposto a questa domanda che stavo cercando di trovare is_complete modello in Boost e ci siamo resi conto che non esiste un modello simile in Boost.TypeTraits. Perché non esiste tale modello nella libreria Boost? Come dovrebbe essere?

//! Check whether type complete
template<typename T>
struct is_complete
{   
  static const bool value = ( sizeof(T) > 0 );
};

...

// so I could use it in such a way
BOOST_STATIC_ASSERT( boost::is_complete<T>::value );

Il codice sopra riportato non è corretto, poiché è illegale applicare sizeof a un tipo incompleto. Quale sarà una buona soluzione? È possibile applicare SFINAE in questo caso in qualche modo?


Bene, questo problema non può essere risolto in generale senza violare la regola ODR , ma esiste una specifica della piattaforma che funziona per me .

È stato utile?

Soluzione

La risposta data da Alexey Malistov può essere usata su MSVC con una piccola modifica:

namespace 
{
    template<class T, int discriminator>
    struct is_complete {  
      static T & getT();   
      static char (& pass(T))[2]; 
      static char pass(...);   
      static const bool value = sizeof(pass(getT()))==2;
    };
}
#define IS_COMPLETE(X) is_complete<X,__COUNTER__>::value

Sfortunatamente, la macro predefinita COUNTER non fa parte dello standard, quindi non funzionerebbe su tutti i compilatori.

Altri suggerimenti

template<class T>
struct is_complete {
    static T & getT();
    static char (& pass(T))[2];
    static char pass(...);

    static const bool value = sizeof(pass(getT()))==2;
};

Potrebbe essere un po 'tardi, ma finora nessuna soluzione C ++ 11 ha funzionato per tipi sia completi che astratti.

Quindi, eccoti qui.

Con VS2015 (v140), g ++ > = 4.8.1, clang > = 3.4, funziona:

template <class T, class = void>
struct IsComplete : std::false_type
{};

template <class T>
struct IsComplete< T, decltype(void(sizeof(T))) > : std::true_type
{};

Grazie a Bat-Ulzii Luvsanbat: https://blogs.msdn.microsoft.com/vcblog/2015/12/02/partial-support-for-expression-sfinae-in-vs-2015-update -1 /

Con VS2013 (V120):

namespace Details
{

    template <class T>
    struct IsComplete
    {
        typedef char no;
        struct yes { char dummy[2]; };

        template <class U, class = decltype(sizeof(std::declval< U >())) >
        static yes check(U*);

        template <class U>
        static no check(...);

        static const bool value = sizeof(check< T >(nullptr)) == sizeof(yes);
    };

} // namespace Details


template <class T>
struct IsComplete : std::integral_constant< bool, Details::IsComplete< T >::value >
{};

Questo è ispirato a Internet e afferma staticamente che il tipo di modello T non è completo?

Temo che non sia possibile implementare tali tratti di tipo is_complete. L'implementazione fornita da @Alexey non riesce a compilare su G ++ 4.4.2 e G ++ 4.5.0:

  

errore: inizializzazione dell'argomento 1 di & # 8216; carattere statico (& amp; is_complete :: pass (T)) [2] [con T = Foo] & # 8217;

Sul mio Mac, con G ++ 4.0.1 che valuta is_complete<Foo>::value dove struct Foo; è un rendimento incompleto di true che è anche peggio di un errore del compilatore.

T può essere completo e incompleto nello stesso programma, a seconda dell'unità di traduzione ma è sempre dello stesso tipo. Di conseguenza, come commentato sopra, is_complete<T> è sempre dello stesso tipo.

Quindi, se rispetti ODR non è possibile avere __COUNTER__ una valutazione diversa valori che dipendono da dove viene utilizzato; altrimenti significherebbe che hai definizioni diverse per is_complete<T, int> che ODR proibisce.

EDIT: come risposta accettata, io stesso ho hackerato una soluzione che utilizza la macro IS_COMPLETE per creare un'istanza di un tipo <=> diverso ogni volta che viene utilizzata la macro <=>. Tuttavia, con gcc, non sono riuscito a far funzionare SFINAE in primo luogo.

Per risolvere questo problema è necessario eseguire il calcolo nell'argomento predefinito del modello di tratto, poiché il tentativo di modificare la definizione di un modello viola la regola ODR (sebbene una combinazione di __COUNTER__ e namespace {} possa aggirare ODR).

Questo è scritto in C ++ 11 ma può essere adattato per funzionare in modalità C ++ 03 di un compilatore moderatamente recente compatibile con C ++ 11.

template< typename t >
typename std::enable_if< sizeof (t), std::true_type >::type
is_complete_fn( t * );

std::false_type is_complete_fn( ... );

template< typename t, bool value = decltype( is_complete_fn( (t *) nullptr ) )::value >
struct is_complete : std::integral_constant< bool, value > {};

Demo online.

L'argomento predefinito viene valutato in base al nome del modello, quindi può passare contestualmente tra definizioni diverse. Non è necessaria una diversa specializzazione e definizione ad ogni utilizzo; ne hai bisogno solo per true e uno per false.

La regola è data in & # 167; 8.3.6 / 9, che si applica ugualmente agli argomenti predefiniti della funzione e agli argomenti del modello predefiniti:

  

Gli argomenti predefiniti vengono valutati ogni volta che viene chiamata la funzione.

Ma attenzione, l'uso di questo all'interno di un modello è quasi sicuro di violare l'ODR. Un modello creato un'istanza su un tipo incompleto non deve fare diversamente se fosse istanziato su un tipo completo. Personalmente lo voglio solo per un static_assert.

Per inciso, questo principio può essere utile anche se vuoi andare dall'altra parte e implementa la funzionalità di <=> usando template e overload.

Basta intervenire per segnalare che una risposta (non fornita da me) a una domanda non correlata fornisce una soluzione per il is_complete<T> modello.

La risposta è qui . Non lo sto incollando di seguito per non ottenere erroneamente credito per esso.

Non riesco a trovare nulla nello standard che garantisca che la dimensione di un tipo incompleto produrrà 0. Tuttavia, richiede che se T è incompleto ad un certo punto, ma completato più avanti in quell'unità di traduzione, che tutti i riferimenti a T si riferiscono allo stesso tipo - quindi, mentre lo leggo, anche se T è incompleto nel punto in cui è stato invocato il modello, sarebbe necessario dire che era completo se T è stato completato da qualche parte in quell'unità di traduzione.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top