Проблема с вложенным шаблоном в C ++ - PullRequest
8 голосов
/ 25 марта 2019

GCC 7.3.1 компилирует приведенный ниже код, а clang 8.0.0 - нет.Я хотел бы знать, допустим ли этот синтаксис (в этом случае я сообщу о нем как о возможной ошибке лязга).

Спасибо за вашу помощь.

template<typename FOO>
struct Foo
{
  using Value = int;

  template<Value VALUE>
  struct Bar;
};

template<typename FOO>
template<typename Foo<FOO>::Value VALUE>
struct Foo<FOO>::Bar { static void test(); };

template<typename FOO>
template<typename Foo<FOO>::Value VALUE>
void Foo<FOO>::Bar<VALUE>::test() {}

int main() { return 0; }

Сообщение об ошибке clangвыглядит следующим образом:

error: nested name specifier 'Foo<FOO>::Bar<VALUE>::' for declaration does not refer into a class, class template or class template partial specialization
void Foo<FOO>::Bar<VALUE>::test() {}
     ~~~~~~~~~~~~~~~~~~~~~~^
1 error generated.

РЕДАКТИРОВАТЬ: лягушить сообщение об ошибке здесь .

Ответы [ 2 ]

2 голосов
/ 25 марта 2019

Начиная с [temp.mem.class / 1] , мы имеем

Класс члена шаблона класса может быть определен вне определения шаблона класса, в котором он находится.объявляется.

Более того, в не шаблонном контексте [class.nest / 2] сообщает нам:

Функции-члены и статические данныечлены вложенного класса могут быть определены в области имен, включающей определение их класса.

Давайте, следовательно, построим более простой пример и убедимся, что определение функции-членаВложенный тип может быть отделен от определения самого вложенного типа non-template .По аналогии с типами в вашем фрагменте:

template <class FOO>
struct Foo {
   // Simpler, Bar is not a template
   struct Bar;
};

// Definition of Bar outside of Foo as before
template <class FOO>
struct Foo<FOO>::Bar {
   static void test(); 
};

А теперь критическая часть, определение Bar::test() вне самого Bar:

template <class FOO>
void Foo<FOO>::Bar::test() { }

Это счастливо компилируется си gcc-8 и clang (транк, а также гораздо более старая стабильная версия).

Возможно, я что-то здесь неправильно понимаю, но я пришел к выводу, что синтаксис для определения Foo::Bar::test() вне Fooи вне Bar действительно хорошо, и clang должен скомпилировать его как gcc.

1 голос
/ 25 марта 2019

Это интересный случай! Моя позиция, будь то компилятор или стандартная проблема, похожа на @lubgr, но я хотел бы добавить еще несколько идей.

У ICC также есть некоторые проблемы с вашей конструкцией, из-за чего можно предположить, что это более глубоко укоренилось в стандарте (тем не менее, gcc здесь может быть правильным). Ошибка завершается ошибкой: «список аргументов шаблона должен соответствовать списку параметров» - это может означать, что для обоих компиляторов это:

template<typename FOO>
template<typename Foo<FOO>::Value VALUE>

не идентичны оригинальному определению Foo. Кажется, это ошибка обоих компиляторов, но я научился быть осторожным, когда два разных компилятора имеют общие проблемы.

Извлечение определения Value из исходного шаблона в отдельный шаблон исправляет регистр ( код в Compiler Explorer ):

template<typename T>
struct X
{
    using Value = int;
};

template<typename FOO>
struct Foo
{    
  template<typename X<FOO>::Value VALUE>
  struct Bar;
};

template<typename FOO>
template<typename X<FOO>::Value VALUE>
struct Foo<FOO>::Bar { static void test(); };

template<typename FOO>
template<typename X<FOO>::Value VALUE>
void Foo<FOO>::Bar<VALUE>::test() {}

int main() { return 0; }

Вы также можете это исправить, просто используя жестко закодированный тип Value ( код в Compiler Explorer ) - но, вероятно, это не то, что вам нужно:

template<typename FOO>
struct Foo
{    
  template<int VALUE>
  struct Bar;
};

template<typename FOO>
template<int VALUE>
struct Foo<FOO>::Bar { static void test(); };

template<typename FOO>
template<int VALUE>
void Foo<FOO>::Bar<VALUE>::test() {}

int main() { return 0; }

Надеюсь, это поможет!

...