оператор преобразования, который ссылается на параметры шаблона класса и члены класса constexpr - PullRequest
0 голосов
/ 09 июня 2019
#include <cstddef>

template <class T, std::size_t rank_>
struct  B { };

template <class T, std::size_t rank_>
struct  A {
    static constexpr auto rank = rank_;
    operator B<T, rank>() noexcept;
};

template <class T, std::size_t rank>
A<T, rank>::operator B<T, rank>() noexcept { return {}; }

Обратите внимание, что rank_ - это параметр шаблона класса A, а rank - это константа времени компиляции, которая является членом A.

  1. rank используется в объявлении оператора преобразования

    • g ++ и clang компилируются без ошибок.
    • MSVC 19.20 дает unable to match definition to an existing declaration
  2. rank_ используется в объявлении оператора преобразования

    • объявление изменено с operator B<T, rank>() noexcept; на operator B<T, rank_>() noexcept;
    • г ++ дает no declaration matches A<T, rank>::operator B<T, rank>
    • лязг дает out-of-line definition of operator B<type-parameter-0-0, rank> does not match any declaration in A<T, rank_>
    • MSVC компилируется без ошибок

  1. Кто прав?
  2. Что такое портативное решение проблемы?

Благодаря Artyer, изменение имени символа с rank на rank_ в определении оператора решает проблему. Это могло произойти из-за неоднозначности между параметром шаблона с именем rank и членом класса rank. Компиляторы выполняют поиск имени по-разному.

Годболт Ссылка: https://godbolt.org/z/6oFdrf

1 Ответ

3 голосов
/ 09 июня 2019

Кто прав?

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_ позволяет использовать три компиляторав вашем вопросе принять код.

...