Pergunta

Eu tenho um problema com os indicadores de membros. O código a seguir falha em compilar usando o CC do Oracle Solaris Studio 12.2 e o Cygwin GCC 4.3.4, mas funciona com o Microsoft Visual C ++ 2010:

struct A {
  int x;
};

struct B : public A {
};

template<typename T> class Bar {
public:
  template<typename M> void foo(M T::*p);
};

int main(int, char *[]) {
    Bar<B> bbar;
    bbar.foo(&B::x);
    return 0;
}

Na linha próxima da última linha, ambos os compiladores mencionados acima não conseguem encontrar uma correspondência para Bar<B>::foo(int A::*). Eu escrevi um teste simples para confirmar que o tipo de expressão &B::x é na verdade int A::*:

// ...

static void foo(int A::*p) {
  std::cout << "A" << std::endl;
}

static void foo(int B::*p) {
  std::cout << "B" << std::endl;
}

int main(int, char *[]) {
    foo(&B::x);  // prints "A", even on MS VC++ 2010 
    return 0;
}

A solução alternativa a seguir funciona com o GCC (ainda não testado com o Oracle CC), mas falha com o VC ++ devido à ambiguidade:

template<typename T> class Bar {
public:
  template<typename M> void foo(M T::*p);
  template<typename M, typename _T_base> inline void foo(M _T_base::*p) {
      foo(static_cast<M T::*>(p));
  }
};

Minha pergunta: qual comportamento está correto? Aparentemente, o VC ++ faz um upcast implícito de int A::* para int B::* Para satisfazer a chamada para o modelo de função do membro, os outros dois compiladores não devem considerar fazer o mesmo?

Foi útil?

Solução

Uma conversão de int A::* para int B::* é permitido, e esse não é o problema. O problema está na dedução do argumento do modelo, como você pode ver se você tenta o seguinte programa que fornece um argumento de modelo <int> por B::foo e compilar e uma função não-membro foo2 que produz o mesmo erro que B::foo antes.

struct A {
  int x;
};

struct B : public A {
};

template <typename T> class Bar {
public:
  template<typename M> void foo(M T::*p);
};

template<typename M> void foo2(M B::*p);

int main(int, char*[]) {
  Bar<B> bbar;
  bbar.foo<int>(&B::x);
  foo2(&B::x); // error, but foo2<int>(&B::x) would work.
  return 0;
}

Eu acho que esta situação não está coberta pelos casos em que o compilador deve deduzir o argumento do modelo <int> sozinho. 14.8.2.1p3:

Em geral, o processo de dedução tenta encontrar valores de argumento do modelo que tornarão o deduzido um idêntico a (após o tipo A ser transformado como descrito acima). No entanto, existem três casos que permitem uma diferença:

  • Se o P original for um tipo de referência, o deduzido a (isto é, o tipo referido pela referência) pode ser mais qualificado pelo CV do que A.
  • A pode ser outro ponteiro ou ponteiro para o tipo de membro que pode ser convertido para deduzido A através de uma conversão de qualificação (conv.qual).
  • Se P é uma classe, e P tem o modelo de formulário-Id, então A pode ser uma classe derivada do deduzido A. Da mesma forma, se p é um ponteiro para uma classe do modelo de formulário, um ponteiro pode ser um ponteiro para uma classe derivada apontada pelos deduzidos A.

Aqui "P" é o tipo de argumento da função de modelo: M B::*p, onde o parâmetro de tipo de modelo M deve ser determinado. "A" é o tipo de argumento real: int A::*. P e A certamente não são uma referência ou uma classe, e o tipo de conversão de ponteiro para membro que precisaríamos que isso funcione não é uma conversão de qualificação (que descreve apenas manipulações constantes/voláteis como X* para const X* ou int X::* para const int X::*).

Portanto, o argumento do modelo não pode ser deduzido e você deve adicionar o <int> Parâmetro de modelo explícito para o seu código.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top