Тип указателя на члена из базового класса

StackOverflow https://stackoverflow.com/questions/3859517

  •  27-09-2019
  •  | 
  •  

Вопрос

У меня есть проблема в отношении указателей членов. Следующий код не скомпилируется с использованием как Oracle Solaris Studio 12.2 CC и Cygwin GCC 4.3.4, но работает с 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;
}

В следующем к последней строке обе упомянутые выше компиляторы не смогут найти совпадение для Bar<B>::foo(int A::*). Отказ Я написал простой тест, чтобы подтвердить, что тип выражения &B::x на самом деле 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;
}

На следующий обходной путь работает с GCC (не тестирован с Oracle CC еще), но не удается с VC ++ из-за двусмысленности:

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));
  }
};

Мой вопрос: какое поведение правильное? По-видимому, VC ++ делает неявный ускол из int A::* к int B::* Чтобы удовлетворить призыв к шаблону функции участника, не должны ли два других компилятора, которые считают то же самое?

Это было полезно?

Решение

Преобразование из int A::* к int B::* Допускается, и это не проблема. Проблема находится в вычете аргумента шаблона, как вы можете увидеть, попробуете ли вы следующую программу, которая подает аргумент шаблона <int> для B::foo и компилирует, а функция не член foo2 что производит ту же ошибку, что и B::foo сделал раньше.

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;
}

Я думаю, что эта ситуация не покрыта случаями, когда компилятор должен вывести аргумент шаблона <int> самостоятельно. 14.8.2.1P3:

В целом, процесс вычета пытается найти значения аргумента шаблона, которые сделают вывод идентичны (после того, как тип A преобразуется, как описано выше). Однако есть три случая, которые позволяют разницу:

  • Если оригинал P - тип ссылочного типа, выведенный A (т. Е. Тип, упомянутый ссылкой), может быть более CV-квалифицированным, чем A.
  • A может быть другой указатель или указатель на тип члена, который может быть преобразован в выведенный A через квалификационную конверсию (CORV.QUAL).
  • Если P - класс, и P имеет шаблон формы, то может быть полученный класс выведенного А. Аналогично, если P является указателем на класс шаблона формы-идентификатора, A может быть указателем на Полученный класс, указанный на выведенном А.

Здесь «P» - тип аргумента функции шаблона: M B::*p, где параметр шаблона типа M должен быть определен. «A» - это тип фактического аргумента: int A::*. Отказ P и A, безусловно, не является ссылкой или классом, а в своем роде преобразования указателя к участникам нам понадобится для этого, не является преобразованием квалификации (что описывает только консте / волатильные манипуляции, такие как X* к const X* или int X::* к const int X::*).

Таким образом, аргумент шаблона не может быть выведен, и вы должны добавить <int> Явный параметр шаблона в свой код.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top