Преобразование указателя-к-члену базы в указатель-к-члену
Упрощенный пример из недавнего сообщения в блоге:
struct B { void f(); };
struct D : B { };
constexpr auto as_d = static_cast<void(D::*)()>(&D::f); // (1)
template <void (D::*)()>
struct X { };
X<as_d> x; // (2)
gcc, clang и MSVC все принимают объявление as_d
помеченного (1)
. gcc и clang оба отклоняют объявление x
помечено (2)
, но MSVC принимает его.
Оба сообщения gcc и clang указывают, что они знают, что as_d
является указателем на элемент B
лязг:
<source>:9:3
: ошибка: извините, аргумент шаблона типа не указателя типа void (D::*)()
который относится к члену B::f
другого класса, еще не поддерживается
НКА:
<source>:9:7
: error: void (D::*)(){((void (D::*)())B::f), 0}
не является допустимым аргументом шаблона для типа void (D::*)()
Кто прав? Если gcc/clang, каково правило, от которого мы справляемся? Кажется, что as_d
является преобразованным постоянным выражением типа void (D::*)()
для меня...
Ответы
Ответ 1
Ну, это оказалось довольно интересным. Что касается языка, программа действительна - as_d
соответствует требованию быть допустимым аргументом шаблона не-типа (это преобразованное константное выражение правильного типа).
Тем не менее, ABI Itanium C++ ABI, по- видимому , не указывает на искажение для этой ситуации (то есть имеет аргумент шаблона не-типа, тип которого является указателем-на-член-к-производному, но значение которого представляет собой указатель- член к основанию). Компиляторы, нацеленные на то, что ABI (т.е. clang и gcc), в результате, не могут принять этот код. Это объясняет, почему ошибка clang "извините, еще не", а не "нет, плохо!"
С другой стороны, другие ABI не имеют такой проблемы с манипуляцией, и поэтому MSVC и ICC могут скомпилировать программу просто отлично.