2015-04-21 14 views
10

Mi arrendo, aiutaci a spiegare questo comportamento. L'esempio che presentiamo di seguito è il più semplice a cui potrei pensare, ma riassume il problema (usando g ++ 4.9.2 su Cygwin con C++ 14 abilitato). Voglio creare una classe che si comporterà come std::mem_fn. Qui è la mia classe:.Il typedef interno cambia in base alla classe genitore

template <class R, class T, R(T::*P)() const > 
struct property { 

    static R get(const T& t) { 
     return (t.*P)(); 
    } 
}; 

dove R è il tipo di ritorno e T è il tipo di oggetto sono interessanti nel parametro terzo modello è un puntatore a funzione membro. Fin qui tutto bene.

ho quindi creare una semplice classe che detiene un intero come segue

class data_class { 

public: 

    unsigned get_data() const { 
     return m_data; 
    } 

private: 
    unsigned m_data; 
}; 

Questa è la classe che verrà utilizzato nella classe property mostrato in precedenza.

Ora creare due classi che ereditano da data_class come segue

struct my_classA 
: public data_class { 

    using data = property<unsigned, data_class, &data_class::get_data>; 
}; 

//same as my_classA, only templated 
template <int I> 
struct my_classB 
: public data_class { 

    using data = property<unsigned, data_class, &data_class::get_data>; 
}; 

Hanno la stessa identica typedef interna, ma my_classB è basato su modelli. Ora i seguenti tipi dovrebbero in teoria essere lo stesso:

using target_t = property<unsigned, data_class, &data_class::get_data>; 
using test1_t = typename my_classA::data; 
using test2_t = typename my_classB<1>::data; 

Tuttavia il mio compilatore dice che solo test1_t e target_t sono gli stessi. Il tipo dedotto per test2_t è apparentemente

property<unsigned int, data_class, (& data_class::get_data)> > 

dove questo tipo ha queste staffe intorno al puntatore a funzione membro. Perché lo test2_t non è lo stesso di target_t? Ecco il codice completo nel caso in cui tu voglia provarlo sul tuo sistema. Ogni aiuto è molto apprezzato.

#include <type_traits> 

class data_class { 

public: 

    unsigned get_data() const { 
     return m_data; 
    } 

private: 
    unsigned m_data; 
}; 

//takes return type, class type, and a pointer to member function 
//the get function takes an object as argument and uses the above pointer to call the member function 
template <class R, class T, R(T::*P)() const > 
struct property { 

    static R get(const T& t) { 
     return (t.*P)(); 
    } 
}; 

struct my_classA 
: public data_class { 

    using data = property<unsigned, data_class, &data_class::get_data>; 
}; 

//same as my_classA, only templated 
template <int I> 
struct my_classB 
: public data_class { 

    using data = property<unsigned, data_class, &data_class::get_data>; 
}; 

//used to produce informative errors 
template <class T> 
struct what_is; 

//all 3 types below should, in theory, be the same 
//but g++ says that test2_t is different 
using target_t = property<unsigned, data_class, &data_class::get_data>; 
using test1_t = typename my_classA::data; 
using test2_t = typename my_classB<1>::data; 

static_assert(std::is_same<target_t, test1_t>::value, ""); //this passes 
static_assert(std::is_same<target_t, test2_t>::value, ""); //this does not 

int main() { 

    what_is<test1_t> t1; 
    what_is<test2_t> t2; 
} 
+1

Provare effettivamente usando 'test2_t' (come dichiarare una variabile di quel tipo) per ottenere * molto * messaggi confusi:" errore: '& data_class :: get_data' non è un argomento modello valido per tipo 'unsigned int (data_class: : *)() const '"," errore: deve essere un puntatore-membro del modulo' & X :: Y '". – hvd

+2

Funziona in GCC <= 4.8 e in Clang. Sembra essere una regressione in 4.9+. –

+1

@ T.C: Grazie per questo. Potete fornire un link al bug report, per favore? Inseriscilo anche come risposta in modo che io possa accettarlo :) – linuxfever

risposta

1

Ho eseguito il codice con C++ 11 perché non ho ancora molta familiarità con C++ 14. Ma tutto ciò che ho sostituito sono stati l'utilizzo (alias) con typedefs e semplificato un po 'il codice. Nulla da influenzare la sua produzione.

Ho ottenuto i risultati desiderati aggiungendo un typname T al modello di classe B ereditato che, una volta istanziato, sostituirà R con T, quindi in questo caso "unsigned".

#include <iostream> 
#include <type_traits> 

template <typename R, typename T, R(T::*P)() const> 
struct property 
{ 
    static R get(const T& t) 
    { 
     return (t.*P)(); 
    } 
}; 


struct data_class 
{ 
    private: 
     unsigned m_data; 

    public: 
     unsigned get_data() const 
     { 
      return m_data; 
     } 
}; 

struct my_classA : public data_class 
{ 
    typedef property<unsigned, data_class, &data_class::get_data> data; 
}; 

template <typename T, int> 
struct my_classB : public data_class 
{ 
    typedef property<T, data_class, &data_class::get_data> data; 
}; 


int main() 
{ 

    typedef typename my_classA::data normClassA; 
    typedef typename my_classB<unsigned,1>::data tmplClassB; 

    std::cout<< std::is_same< property<unsigned, data_class, &data_class::get_data> , normClassA >::value <<std::endl; 
    std::cout<< std::is_same< property<unsigned, data_class, &data_class::get_data> , tmplClassB >::value <<std::endl; 

} 

Il risultato è questo:

~$g++ -std=c++11 test.cpp 
~$./a.out 
1 
1 

penso che il problema ha a che fare con i criteri di istanza modello di classe, perché quando ho inizialmente provato a stampare i sizeof del delle due classi, my_classA :: dati restituito 1, ma my_classB < 1> :: data terminata in un errore del compilatore. Sono ancora piuttosto confuso sul motivo per cui ciò si verifica. Tecnicamente dovrebbe aver istanziato il modello di classe bene. Forse è la proprietà all'interno del template classB che è stato falsamente istanziato. Guarderò più in questo, ma se trovi la risposta, per favore pubblicala. È interessante!

MODIFICA: Il codice originale funziona correttamente su Cygwin GCC 4.8.2. Il risultato è 1 e 1. Forse è solo un problema del compilatore gcc4.9.2.

Problemi correlati