Кто прав?
Clang и GCC верны на всех аккаунтах.Причина, по которой ваше измененное определение становится плохо сформированным, представляет собой интересную смесь предложений.Сначала я назову их, а потом объясню.
[temp.local]
7 В определении членашаблона класса, который появляется вне определения шаблона класса, имя члена шаблона класса скрывает имя параметра-шаблона любых входящих в него шаблонов классов (но не параметр-шаблона члена, если член являетсяшаблон класса или функции).[Пример:
template<class T> struct A {
struct B { /* ... */ };
typedef void C;
void f();
template<class U> void g(U);
};
template<class B> void A<B>::f() {
B b; // A's B, not the template parameter
}
template<class B> template<class C> void A<B>::g(C) {
B b; // A's B, not the template parameter
C c; // the template parameter C, not A's C
}
- конец примера]
[temp.over.link]
4 КогдаВыражение, которое ссылается на параметр шаблона, используется в списке параметров функции или типе возврата в объявлении шаблона функции, выражение, которое ссылается на параметр шаблона, является частью сигнатуры шаблона функции.Это необходимо для того, чтобы объявление шаблона функции в одном модуле перевода можно было связать с другим объявлением шаблона функции в другом модуле перевода, и, наоборот, чтобы гарантировать, что шаблоны функций, предназначенные для различения, не связаны друг с другом.,[Пример:
template <int I, int J> A<I+J> f(A<I>, A<J>); // #1
template <int K, int L> A<K+L> f(A<K>, A<L>); // same as #1
template <int I, int J> A<I-J> f(A<I>, A<J>); // different from #1
- конец примера] [Примечание: Большинство выражений, использующих параметры шаблона, используют нетипизированные параметры шаблона, но выражение может ссылаться на параметр типа.Например, параметр типа шаблона может использоваться в операторе sizeof.- примечание конца]
Таким образом, в объявлении из opertor B<T, rank>
id-выражение rank
(A<T, rank_>::rank
) является частью сигнатуры оператора, посколькуон используется в типе возврата (тип возврата оператора преобразования неявно задается как часть его имени) и ссылается на параметр шаблона.
Когда вы изменили operator B<T, rank>() noexcept;
на operator B<T, rank_>() noexcept;
, вы изменилиподпись оператора!Теперь определение вне класса не соответствует, потому что вы все еще пытаетесь использовать id-Experssion rank
в сигнатуре, поскольку член класса скрывает параметр шаблона после ::
.
template <class T, std::size_t rank>
A<T, rank>::operator B<T, rank>() noexcept { return {}; }
// ^- This is the member of A, not the name
// of the parameter above it
* 1044.*
Что такое переносимое решение проблемы?
Не использование члена rank
в качестве аргумента шаблона или имени параметра, а вместо этого выбор везде rank_
позволяет использовать три компиляторав вашем вопросе принять код.