class A { int a; };
template<typename, typename = void>
class test {};
template<typename T>
class test<T,decltype(T::a)> {};
int main() { test<A> a; }
Приведенный выше код компилируется без ошибок в clang version 3.8.0-2ubuntu4 (tags/RELEASE_380/final)
, но не скомпилируется на g++-5 (Ubuntu 5.4.1-2ubuntu1~16.04) 5.4.1 20160904
и g++-6 (Ubuntu 6.2.0-3ubuntu11~16.04) 6.2.0 20160901
с такими ошибками:
main.cpp: In function ‘int main()’:
main.cpp:9:22: error: ‘int A::a’ is private within this context
int main() { test<A> a; }
^
main.cpp:1:15: note: declared private here
class A { int a; };
В обоих случаях я скомпилирован с -std=c++11
, но эффект тот же для -std=c++14
и -std=c++1z
.
Какой компилятор здесь правильно? Я предположил, что по крайней мере с тех пор, как С++ 11, доступ к закрытым членам во время замены шаблона должен вызвать SFINAE, подразумевая, что clang является правильным, а gcc - нет. Есть ли какое-то дополнительное правило, о котором я не знаю?
Для справки, я думаю о примечании в §14.8.2/8 нынешнего стандартного проекта N4606:
Если подстановка приводит к недопустимому типу или выражению, введите вычет заканчивается. Недопустимым типом или выражением является тот, который был бы плохо сформированный, с требуемой диагностикой, если он написан с использованием замещенные аргументы. [Примечание: если диагностика не требуется, программа по-прежнему плохо сформирована. Проверка доступа выполняется как часть процесс замещения. - конечная нота]
Использование функции-члена и указателя функции-члена вместо этого принимается обоими компиляторами:
class A { void a() {}; };
template<typename, typename = void>
class test {};
template<typename T>
class test<T,decltype(&T::a)> {};
int main() { test<A> a; }