Статический член шаблона шаблона

У меня есть проблема с этим фрагментом кода:

template <typename T>
struct S
{
  static int a;
};

template <typename T>
decltype(S<T>::a) S<T>::a;

clang-3.4 говорит:

s.cpp:8:25: error: redefinition of 'a' with a different type: 'decltype(S<T>::a)' vs 'int'
decltype(S<T>::a) S<T>::a;
                        ^
s.cpp:4:14: note: previous definition is here
  static int a;
             ^
1 error generated.

Но gcc-4.8.2 принимает. Какой из компиляторов прав? Должен ли я избегать такого кода в будущем?

Ответы

Ответ 1

Clang требует, чтобы определение соответствовало объявлению во время определения шаблона, тогда как GCC и другие откладывали согласование до момента создания экземпляра (что никогда не случается для вашего примера).

Clang принимает это:

#include <type_traits>

template <typename T>
struct S
{
  static int a;
};

template <typename T>
typename std::enable_if< true, int >::type S<T>::a; // Resolves before instantiation

но отклоняет это небольшое изменение:

template <typename T>
typename std::enable_if< std::is_same< T, T >::value, int >::type S<T>::a;

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

С помощью правила looser, которое GCC, по-видимому, применяет, вы можете иметь две объявления участников и два определения, но каждое определение может завершать любое из объявлений в зависимости от параметров шаблона.

Код, который принимает GCC и MSVC, плохо сформирован, не требуется никакого диагноза... до наступления фактического стандартного погребения где-то в § 3 [основной], §7 [dcl.dcl], §8 [dcl.decl], §14 [temp], или, может быть, где-то еще.


Я до сих пор не могу найти, какое правило соответствует определениям объектов перед предыдущими объявлениями, но в §14.4/2 указано, что decltype(…) не может быть эквивалентным (я предполагаю, что в декларативном смысле) int.

Если выражение e включает параметр шаблона, decltype(e)обозначает уникальный зависимый тип. Два таких указателя-указателя ссылаются для того же типа, только если их выражения эквивалентны (14.5.6.1). [Примечание: однако, это может быть псевдонимом, например, с помощью typedef-name. - конец примечание]

Я уверен, что эквивалентность, а не простое наложение, необходимо, чтобы определение соответствовало декларации. §14.5.6.1 довольно глубоко проникает на эту территорию, за исключением того, что она специально обсуждает сигнатуры функций.

Ответ 2

Я думаю, что Кланг может быть прав, отвергая это. 14.2p2 говорит о decltype(e)

Если выражение e включает параметр шаблона, decltype (e) обозначает уникальный зависимый тип.

В DR # 2, трассировка обсуждения говорит

Мое мнение (которое, по моему мнению, совпадает с несколькими опубликованными на рефлекторе в последнее время) заключается в том, что определение вне класса должно соответствовать объявлению в шаблоне.

...

В общем случае, если вы можете сопоставить объявления с использованием только информации из шаблона, то объявление действительно.

Я думаю, что он по-прежнему совпадает, если один из них использует typedef (как показано в DR), потому что S<T>::type является членом текущего экземпляра, и тип aliased может быть просмотрен напрямую. Но decltype(e), как указано выше, всегда будет обозначать уникальный тип (во время разбора шаблона), кроме как в отношении другого decltype(e), который указывает эквивалентное выражение.


Почему я сказал, может? Из-за 14.6p8

Никакая диагностика не должна выдаваться для шаблона, для которого может быть создана действительная специализация.

Можно было прочитать это, указав, что проверка эквивалентности типов просто задерживается до момента создания экземпляра. Это, однако, противоречило бы дискуссиям в DR, о которых я думаю, потому что они говорят: "Если вы можете сопоставлять объявления с использованием только информации из шаблона, тогда объявление действительно" (и я предполагаю, что автор этого заявления имел в виду быть исчерпывающим в ситуациях, когда выражение действительно).

Ответ 3

Для меня здесь сломан clang.

Все комбинации с decltype не будут выполнены. Без decltype он работает.

template <typename T>
struct S
{   
      static int a;

      using type = decltype( a );
      typedef decltype( a ) type2;
};  

template <typename T>
1) decltype(S<T>::a) S<T>::a;
2) int S<T>::a;
3) typename S<T>::type S<T>::a;
4) typename S<T>::type2 S<T>::a;

1 gcc работает, clang терпит неудачу

2 gcc + clang works

3 gcc работает, clang fail

4 gcc работает, clang fail

Я сделал еще несколько попыток решить проблему, но не смог добиться успеха.

Есть еще несколько дискуссий по таким проблемам: С++ Статичная членная инициализация (шаблонное удовольствие внутри)

Изменить: Я обнаружил, что эта тема до сих пор просто "не решена" в стандарте, и clang не реализовал ее: Взгляни на: http://clang.llvm.org/cxx_dr_status.html (пункт 205)

Надеюсь, что я не понял эту страницу неправильно. Не стесняйтесь исправить мою интерпретацию.