Проблема с вложенным шаблоном C

#c #templates #g #language-lawyer #clang

#c #шаблоны #g #язык-юрист #clang

Вопрос:

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

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

 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.
  

Редактировать:
Здесь появляется сообщение о возможной ошибке clang.

Комментарии:

1. Существует несколько основных проблем, связанных с эквивалентностью типов шаблонов и псевдонимами (например, 1979 ). Может быть связано.

Ответ №1:

Из [temp.mem.class/1] мы имеем

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

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

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

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

 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 и outside of Bar действительно хорош, и clang его следует скомпилировать так, как это gcc делается.

Ответ №2:

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

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

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

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

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

 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 тип (код в проводнике компилятора) — но это, вероятно, не то, что вам нужно:

 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; }
  

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

Комментарии:

1. Спасибо, ваш первый пример был полезен для лучшего понимания проблемы.