Pergunta

Para criar função de modelo algoritmo que eu preciso saber se x ou X (e y ou Y) na classe que é o argumento de modelo. Ele pode, por útil ao usar a minha função para a classe MFC CPoint ou classe GDI + PointF ou alguns outros. Todos eles utilizam diferentes x neles. Minha solução poderia ser reduzido para o seguinte código:


template<int> struct TT {typedef int type;};
template<class P> bool Check_x(P p, typename TT<sizeof(&P::x)>::type b = 0) { return true; }
template<class P> bool Check_x(P p, typename TT<sizeof(&P::X)>::type b = 0) { return false; }

struct P1 {int x; };
struct P2 {float X; };
// it also could be struct P3 {unknown_type X; };

int main()
{
    P1 p1 = {1};
    P2 p2 = {1};

    Check_x(p1); // must return true
    Check_x(p2); // must return false

    return 0;
}

Mas não compilar no Visual Studio, ao compilar no GNU C ++. Com o Visual Studio que eu poderia usar o modelo a seguir:


template<class P> bool Check_x(P p, typename TT<&P::x==&P::x>::type b = 0) { return true; }
template<class P> bool Check_x(P p, typename TT<&P::X==&P::X>::type b = 0) { return false; }

Mas não compila no GNU C ++. Existe solução universal?

UPD: Estruturas de P1 e P2 aqui são apenas para exemplo. Há poderia ser qualquer classes com membros desconhecidos.

P.S. Por favor, não publique C ++ 11 soluções aqui porque eles são óbvias e não é relevante para a questão.

Foi útil?

Solução

Outra maneira é este, que se baseia em SFINAE para expressões também. Se o nome de pesquisa resulta em ambigüidade, o compilador irá rejeitar o modelo

template<typename T> struct HasX { 
    struct Fallback { int x; }; // introduce member name "x"
    struct Derived : T, Fallback { };

    template<typename C, C> struct ChT; 

    template<typename C> static char (&f(ChT<int Fallback::*, &C::x>*))[1]; 
    template<typename C> static char (&f(...))[2]; 

    static bool const value = sizeof(f<Derived>(0)) == 2;
}; 

struct A { int x; };
struct B { int X; };

int main() { 
    std::cout << HasX<A>::value << std::endl; // 1
    std::cout << HasX<B>::value << std::endl; // 0
}

É baseado em uma idéia brilhante de alguém para download direto.

Nota: cheques HasX para quaisquer dados ou função de membro chamada x, com tipo arbitrário. O único propósito de introduzir o nome do membro é ter uma possível ambigüidade para pesquisa member-name - o tipo do membro não é importante.

Outras dicas

Aqui está uma solução mais simples do que Johannes Schaub - litb 's um . Ela exige C ++ 11.

#include <type_traits>

template <typename T, typename = int>
struct HasX : std::false_type { };

template <typename T>
struct HasX <T, decltype((void) T::x, 0)> : std::true_type { };

Atualizar :. Um exemplo rápido ea explicação sobre como isso funciona

Para estes tipos:

struct A { int x; };
struct B { int y; };

temos HasX<A>::value == true e HasX<B>::value == false. Vamos ver o porquê.

Em primeiro lugar recordar que std::false_type e std::true_type ter um membro static constexpr bool chamado value que está previsto para false e true, respectivamente. Assim, os dois modelos HasX acima herdar este membro. (O primeiro modelo de std::false_type e a segunda de std::true_type.)

Vamos começar simples e, em seguida, avançar passo a passo até chegar ao código acima.

1) Ponto de partida:

template <typename T, typename U>
struct HasX : std::false_type { };

Neste caso, não há nenhuma surpresa:. Deriva HasX de std::false_type e, portanto, HasX<bool, double>::value == false e HasX<bool, int>::value == false

2) Inadimplente U:

// Primary template
template <typename T, typename U = int>
struct HasX : std::false_type { };

Tendo em conta que padrões U para int, Has<bool> realmente significa HasX<bool, int> e, assim, HasX<bool>::value == HasX<bool, int>::value == false.

3) Adicionando uma especialização:

// Primary template
template <typename T, typename U = int>
struct HasX : std::false_type { };

// Specialization for U = int
template <typename T>
struct HasX<T, int> : std::true_type { };

Em geral, graças ao modelo primário, deriva HasX<T, U> de std::false_type. No entanto, existe uma especialização para U = int que deriva de std::true_type. Portanto, HasX<bool, double>::value == false mas HasX<bool, int>::value == true.

Graças ao padrão para U, HasX<bool>::value == HasX<bool, int>::value == true.

4) decltype e uma maneira elegante de dizer int:

Uma pequena digressão aqui, mas, por favor, tenha paciência comigo.

Basicamente (este não é inteiramente correcto), decltype(expression) produz o tipo de expressão . Por exemplo, tem 0 tipo int assim, meios decltype(0) int. Analogamente, 1.2 tem o tipo double e, assim, meios decltype(1.2) double.

Considere uma função com esta declaração:

char func(foo, int);

onde foo é algum tipo de classe. Se f é um objecto do tipo foo, em seguida, meios decltype(func(f, 0)) char (do tipo retornado por func(f, 0)).

Agora, o (1.2, 0) expressão utiliza o operador vírgula (built-in) que avalia os dois sub-expressão na ordem (isto é, em primeiro lugar e, em seguida 1.2 0), descarta o primeiro valor e o segundo resultados em um. Assim,

int x = (1.2, 0);

é equivalente a

int x = 0;

Colocando isso junto com decltype dá esse meio decltype(1.2, 0) int. Não há nada realmente especial sobre 1.2 ou double aqui. Por exemplo, true tem o tipo bool e meios decltype(true, 0) int também.

Que tal um tipo de classe? Para instace, o que faz decltype(f, 0) média? É natural esperar que isso significa que ainda int mas pode não ser o caso. Na verdade, pode haver uma sobrecarga para o operador vírgula semelhante ao func função acima que leva um foo e um int e retorna um char. Neste caso, decltype(foo, 0) é char.

Como podemos evitar o uso de uma sobrecarga para o operador vírgula? Bem, não há nenhuma maneira de sobrecarregar o operador de vírgula por um void operando e podemos lançar qualquer coisa para void. Portanto, decltype((void) f, 0) significa int. Na verdade, moldes (void) f f de foo para void que basicamente não faz nada, mas dizendo que a expressão deve ser considerado como tendo tipo void. Em seguida, o operador de vírgula embutido é usado e os resultados em ((void) f, 0) 0 que tem tipo int. Assim, os meios decltype((void) f, 0) int.

É este elenco é realmente necessário? Bem, se não há nenhuma sobrecarga para o operador vírgula tomar foo e int então isso não é necessário. Nós sempre pode inspecionar o código fonte para ver se há tal operador ou não. No entanto, se este aparecer em um modelo e f tem o tipo V que é um parâmetro de modelo, então ele não é mais clara (ou mesmo impossível saber) se tal sobrecarga para o operador vírgula existe ou não. Para ser genérico lançamos nós de qualquer maneira.

A linha inferior:. decltype((void) f, 0) é uma maneira elegante de dizer int

5) SFINAE:

Esta é toda uma ciência ;-) OK eu estou exagerando, mas não é muito simples também. Então, eu vou manter a explicação para o mínimo.

SFINAE significa Substituição A falha não é um erro. Isso significa que quando um parâmetro do modelo é substituído por um tipo, um código ilegal C ++ pode aparecer, mas, em algumas circunstâncias , em vez de abortar compilação o compilador simplesmente ignora o código incorreto como se ele não estava lá . Vamos ver como ele se aplica ao nosso caso:

// Primary template
template <typename T, typename U = int>
struct HasX : std::false_type { };

// Specialization for U = int
template <typename T>
struct HasX <T, decltype((void) T::x, 0)> : std::true_type { };

Aqui, novamente, decltype((void) T::x, 0) é uma maneira elegante de dizer int mas com a vantagem de SFINAE.

Quando T é substituído com um tipo, uma construção inválida pode aparecer. Por exemplo, bool::x não é válido C ++, portanto substituindo T com bool em rendimentos T::x uma construo inválido. Sob o princípio SFINAE, o compilador não rejeita o código, ele simplesmente ignora (de partes) de TI. Mais precisamente, como temos meios seenHasX<bool> realmente HasX<bool, int>. A especialização para U = int deve ser selecionado, mas, ao mesmo tempo instanciar isso, os achados do compilador bool::x e ignora a especialização de modelo completamente como se ele não existisse.

Neste ponto, o código é essencialmente o mesmo que no caso de (2) acima, em que existe apenas o molde primário. Assim, HasX<bool, int>::value == false.

O mesmo argumento usado para bool vale para B desde B::x é uma construção inválida (B não tem x membro). No entanto, A::x é OK e o compilador vê nenhum problema em instanciar a especialização para U = int (ou, mais precisamente, por U = decltype((void) A::x, 0)). Assim, HasX<A>::value == true.

6) Unnaming U:

Bem, olhando para o código em (5), novamente, vemos que o nome U não é usado em qualquer lugar, mas na sua declaração (typename U). Podemos, então, unname o segundo argumento de modelo e obtemos o código mostrado no topo deste post.

Eu fui redirecionado aqui de um questão href="https://stackoverflow.com/questions/14522939/check-whether-a-field-exists-in-a-struct"> que tem sido fechado como um duplicado desta. Eu sei que é uma discussão antiga, mas eu só queria sugerir uma alternativa (? Mais simples) de implementação que funciona com C ++ 11. Supondo que queremos verificar se uma determinada classe tem uma variável de membro chamado id:

#include <type_traits>

template<typename T, typename = void>
struct has_id : std::false_type { };

template<typename T>
struct has_id<T, decltype(std::declval<T>().id, void())> : std::true_type { };

É isso. E aqui é como ele seria usado ( exemplo vivo ) :

#include <iostream>

using namespace std;

struct X { int id; };
struct Y { int foo; };

int main()
{
    cout << boolalpha;
    cout << has_id<X>::value << endl;
    cout << has_id<Y>::value << endl;
}

As coisas podem ser ainda mais simples com um par de macros:

#define DEFINE_MEMBER_CHECKER(member) \
    template<typename T, typename V = bool> \
    struct has_ ## member : false_type { }; \
    template<typename T> \
    struct has_ ## member<T, \
        typename enable_if< \
            !is_same<decltype(declval<T>().member), void>::value, \
            bool \
            >::type \
        > : true_type { };

#define HAS_MEMBER(C, member) \
    has_ ## member<C>::value

O que poderia ser usado desta maneira:

using namespace std;

struct X { int id; };
struct Y { int foo; };

DEFINE_MEMBER_CHECKER(foo)

int main()
{
    cout << boolalpha;
    cout << HAS_MEMBER(X, foo) << endl;
    cout << HAS_MEMBER(Y, foo) << endl;
}

UPDATE: Eu fiz recentemente um pouco mais com o código eu postei na minha resposta original, então eu estou atualizando esta para explicar as mudanças / adições

.

Aqui estão alguns trechos de uso: * A coragem para tudo isso são mais para baixo

Verificar x membro de uma determinada classe. Poderia ser var, func, classe, união, ou enum:

CREATE_MEMBER_CHECK(x);
bool has_x = has_member_x<class_to_check_for_x>::value;

Verificar função membro void x():

//Func signature MUST have T as template variable here... simpler this way :\
CREATE_MEMBER_FUNC_SIG_CHECK(x, void (T::*)(), void__x);
bool has_func_sig_void__x = has_member_func_void__x<class_to_check_for_x>::value;

Verificar x variável de membro:

CREATE_MEMBER_VAR_CHECK(x);
bool has_var_x = has_member_var_x<class_to_check_for_x>::value;

Verificar x classe membro:

CREATE_MEMBER_CLASS_CHECK(x);
bool has_class_x = has_member_class_x<class_to_check_for_x>::value;

Verificar x união membro:

CREATE_MEMBER_UNION_CHECK(x);
bool has_union_x = has_member_union_x<class_to_check_for_x>::value;

Verificar x membro enum:

CREATE_MEMBER_ENUM_CHECK(x);
bool has_enum_x = has_member_enum_x<class_to_check_for_x>::value;

Verifique se há qualquer função membro x independentemente da assinatura:

CREATE_MEMBER_CHECK(x);
CREATE_MEMBER_VAR_CHECK(x);
CREATE_MEMBER_CLASS_CHECK(x);
CREATE_MEMBER_UNION_CHECK(x);
CREATE_MEMBER_ENUM_CHECK(x);
CREATE_MEMBER_FUNC_CHECK(x);
bool has_any_func_x = has_member_func_x<class_to_check_for_x>::value;

ou

CREATE_MEMBER_CHECKS(x);  //Just stamps out the same macro calls as above.
bool has_any_func_x = has_member_func_x<class_to_check_for_x>::value;

Detalhes e núcleo:

/*
    - Multiple inheritance forces ambiguity of member names.
    - SFINAE is used to make aliases to member names.
    - Expression SFINAE is used in just one generic has_member that can accept
      any alias we pass it.
*/

template <typename... Args> struct ambiguate : public Args... {};

template<typename A, typename = void>
struct got_type : std::false_type {};

template<typename A>
struct got_type<A> : std::true_type {
    typedef A type;
};

template<typename T, T>
struct sig_check : std::true_type {};

template<typename Alias, typename AmbiguitySeed>
struct has_member {
    template<typename C> static char ((&f(decltype(&C::value))))[1];
    template<typename C> static char ((&f(...)))[2];

    //Make sure the member name is consistently spelled the same.
    static_assert(
        (sizeof(f<AmbiguitySeed>(0)) == 1)
        , "Member name specified in AmbiguitySeed is different from member name specified in Alias, or wrong Alias/AmbiguitySeed has been specified."
    );

    static bool const value = sizeof(f<Alias>(0)) == 2;
};

Macros (El Diablo!):

CREATE_MEMBER_CHECK:

//Check for any member with given name, whether var, func, class, union, enum.
#define CREATE_MEMBER_CHECK(member)                                         \
                                                                            \
template<typename T, typename = std::true_type>                             \
struct Alias_##member;                                                      \
                                                                            \
template<typename T>                                                        \
struct Alias_##member <                                                     \
    T, std::integral_constant<bool, got_type<decltype(&T::member)>::value>  \
> { static const decltype(&T::member) value; };                             \
                                                                            \
struct AmbiguitySeed_##member { char member; };                             \
                                                                            \
template<typename T>                                                        \
struct has_member_##member {                                                \
    static const bool value                                                 \
        = has_member<                                                       \
            Alias_##member<ambiguate<T, AmbiguitySeed_##member>>            \
            , Alias_##member<AmbiguitySeed_##member>                        \
        >::value                                                            \
    ;                                                                       \
}

CREATE_MEMBER_VAR_CHECK:

//Check for member variable with given name.
#define CREATE_MEMBER_VAR_CHECK(var_name)                                   \
                                                                            \
template<typename T, typename = std::true_type>                             \
struct has_member_var_##var_name : std::false_type {};                      \
                                                                            \
template<typename T>                                                        \
struct has_member_var_##var_name<                                           \
    T                                                                       \
    , std::integral_constant<                                               \
        bool                                                                \
        , !std::is_member_function_pointer<decltype(&T::var_name)>::value   \
    >                                                                       \
> : std::true_type {}

CREATE_MEMBER_FUNC_SIG_CHECK:

//Check for member function with given name AND signature.
#define CREATE_MEMBER_FUNC_SIG_CHECK(func_name, func_sig, templ_postfix)    \
                                                                            \
template<typename T, typename = std::true_type>                             \
struct has_member_func_##templ_postfix : std::false_type {};                \
                                                                            \
template<typename T>                                                        \
struct has_member_func_##templ_postfix<                                     \
    T, std::integral_constant<                                              \
        bool                                                                \
        , sig_check<func_sig, &T::func_name>::value                         \
    >                                                                       \
> : std::true_type {}

CREATE_MEMBER_CLASS_CHECK:

//Check for member class with given name.
#define CREATE_MEMBER_CLASS_CHECK(class_name)               \
                                                            \
template<typename T, typename = std::true_type>             \
struct has_member_class_##class_name : std::false_type {};  \
                                                            \
template<typename T>                                        \
struct has_member_class_##class_name<                       \
    T                                                       \
    , std::integral_constant<                               \
        bool                                                \
        , std::is_class<                                    \
            typename got_type<typename T::class_name>::type \
        >::value                                            \
    >                                                       \
> : std::true_type {}

CREATE_MEMBER_UNION_CHECK:

//Check for member union with given name.
#define CREATE_MEMBER_UNION_CHECK(union_name)               \
                                                            \
template<typename T, typename = std::true_type>             \
struct has_member_union_##union_name : std::false_type {};  \
                                                            \
template<typename T>                                        \
struct has_member_union_##union_name<                       \
    T                                                       \
    , std::integral_constant<                               \
        bool                                                \
        , std::is_union<                                    \
            typename got_type<typename T::union_name>::type \
        >::value                                            \
    >                                                       \
> : std::true_type {}

CREATE_MEMBER_ENUM_CHECK:

//Check for member enum with given name.
#define CREATE_MEMBER_ENUM_CHECK(enum_name)                 \
                                                            \
template<typename T, typename = std::true_type>             \
struct has_member_enum_##enum_name : std::false_type {};    \
                                                            \
template<typename T>                                        \
struct has_member_enum_##enum_name<                         \
    T                                                       \
    , std::integral_constant<                               \
        bool                                                \
        , std::is_enum<                                     \
            typename got_type<typename T::enum_name>::type  \
        >::value                                            \
    >                                                       \
> : std::true_type {}

CREATE_MEMBER_FUNC_CHECK:

//Check for function with given name, any signature.
#define CREATE_MEMBER_FUNC_CHECK(func)          \
template<typename T>                            \
struct has_member_func_##func {                 \
    static const bool value                     \
        = has_member_##func<T>::value           \
        && !has_member_var_##func<T>::value     \
        && !has_member_class_##func<T>::value   \
        && !has_member_union_##func<T>::value   \
        && !has_member_enum_##func<T>::value    \
    ;                                           \
}

CREATE_MEMBER_CHECKS:

//Create all the checks for one member.  Does NOT include func sig checks.
#define CREATE_MEMBER_CHECKS(member)    \
CREATE_MEMBER_CHECK(member);            \
CREATE_MEMBER_VAR_CHECK(member);        \
CREATE_MEMBER_CLASS_CHECK(member);      \
CREATE_MEMBER_UNION_CHECK(member);      \
CREATE_MEMBER_ENUM_CHECK(member);       \
CREATE_MEMBER_FUNC_CHECK(member)

Boost.ConceptTraits fornece entre outros alguns macros para definir características de tipo, como por exemplo BOOST_TT_EXT_DEFINE_HAS_MEMBER(name), o qual define um tipo de característica de forma a:

has_member_##name<T>

Isto dá verdadeiro se T tem um tipo de membro chamado. Note, no entanto, que isso não irá detectar membros tipo de referência.

No seu caso será suficiente para acrescentar em um arquivo de cabeçalho

BOOST_TT_EXT_DEFINE_HAS_MEMBER_TYPE(x)

e verificação da seguinte maneira

BOOST_STATIC_ASSERT(has_member_x<P1>::value);

A técnica utilizada é a mesma que foi explicado em algumas das respostas anteriores.

Infelizmente esta biblioteca não é mais mantido. Agora que C ++ 0x não vai inclui conceito, esta biblioteca juntamente com SFINAE é um substituto perfeito para trabalhar com a maioria dos conceitos.

Por que você não usar a especialização como esta:

struct P1 {int x; };
struct P2 {int X; };

template<class P> 
bool Check_x(P p) { return true; }

template<> 
bool Check_x<P2>(P2 p) { return false; }

A segunda resposta (litb do) para isso mostra como detectar um membro:

É possível escrever um modelo para verificar para a existência de uma função?

Por que você não apenas criar especializações modelo de Check_x?

template<> bool Check_x(P1 p) { return true; }
template<> bool Check_x(P2 p) { return false; }

Heck, quando penso nisso. Se você só tem dois tipos, por que você ainda precisa de modelos para isso?

são as funções (x, x, y, Y) de uma classe base abstrata, ou poderiam ser reformulado para ser assim? Se assim você pode usar a macro SUPERSUBCLASS () a partir do Design ++ Modern C, juntamente com idéias da resposta a esta pergunta:

de tempo de compilação com base Tipo de expedição

Podemos obter em tempo de compilação: 0 - not_member, 1 - is_object, 2 - is_function para cada classe e membro necessário - objeto ou função: http://ideone.com / Fjm9u5

#include <iostream>
#include <type_traits>

#define IS_MEMBER(T1, M)    \
struct {        \
    struct verystrangename1 { bool M; };    \
    template<typename T> struct verystrangename2 : verystrangename1, public T { }; \
    \
    enum return_t { not_member, is_object, is_function }; \
    template<typename T, typename = decltype(verystrangename2<T>::M)> constexpr return_t what_member() { return not_member;  }  \
    template<typename T> typename std::enable_if<std::is_member_object_pointer<decltype(&T::M)>::value, return_t>::type constexpr what_member() { return is_object; }   \
    template<typename T> typename std::enable_if<std::is_member_function_pointer<decltype(&T::M)>::value, return_t>::type constexpr what_member() { return is_function; }   \
    constexpr operator return_t() { return what_member<T1>(); } \
}

struct t {
    int aaa;
    float bbb;
    void func() {}
};

// Can't be in function
IS_MEMBER(t, aaa) is_aaa_member_of_t;
IS_MEMBER(t, ccc) is_ccc_member_of_t;
IS_MEMBER(t, func) is_func_member_of_t;

// known at compile time
enum { const_is_aaa_member_of_t = (int)is_aaa_member_of_t };
static constexpr int const_is_func_member_of_t = is_func_member_of_t;

int main() {        
    std::cout << std::boolalpha << "0 - not_member, 1 - is_object, 2 - is_function \n\n" <<
        "is aaa member of t = " << is_aaa_member_of_t << std::endl << 
        "is ccc member of t = " << is_ccc_member_of_t << std::endl << 
        "is func member of t = " << is_func_member_of_t << std::endl << 
        std::endl;

    return 0;
}

Resultado:

0 - not_member, 1 - is_object, 2 - is_function 

is aaa member of t = 1
is ccc member of t = 0
is func member of t = 2

Para a classe / struct:

struct t {
    int aaa;
    float bbb;
    void func() {}
};
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top