Ошибка g++ с частичной специализацией шаблонов

Я пишу некоторый TMP-тяжелый код для g++ (версия 4.8.1_1, Macports) и clang++ (версия 3.3, Macports). В то время как g ++ отклоняет следующий листинг кода с UNBRIDLED FURY, clang ++ компилирует его с изяществом и великолепием.

  • Какой компилятор в праве? (Я сильно подозреваю, что это g ++, но я хочу получить некоторое подтверждение от других перед отправкой сообщения об ошибке.)
  • Есть ли у вас какие-либо простые или изящные решения, которые можно предложить? (Мне нужно использовать псевдонимы шаблонов, поэтому переключение на структуры, что заставляет g ++ принимать код, не вариант.)

Вот список кодов, сделанный только для вас.

template <class... Ts>
struct sequence;

template <int T>
struct integer;

// This definition of `extents` causes g++ to issue a compile-time error.
template <int... Ts>
using extents = sequence<integer<Ts>...>;

// However, this definition works without any problems.
// template <int... Ts>
// struct extents;

template <int A, int B, class Current>
struct foo;

template <int A, int B, int... Ts>
struct foo<A, B, extents<Ts...>>
{
    using type = int;
};

template <int B, int... Ts>
struct foo<B, B, extents<Ts...>>
{
    using type = int;
};

int main()
{
    using t = foo<1, 1, extents<>>::type;
    return 0;
}

Вот вывод g ++:

er.cpp: In function 'int main()':
er.cpp:39:41: error: ambiguous class template instantiation for 'struct foo<1, 1, sequence<> >'
  using t = typename foo<1, 1, extents<>>::type;
                                         ^
er.cpp:26:8: error: candidates are: struct foo<A, B, sequence<integer<Ts>...> >
 struct foo<A, B, extents<Ts...>>
        ^
er.cpp:32:8: error:                 struct foo<B, B, sequence<integer<Ts>...> >
 struct foo<B, B, extents<Ts...>>
        ^
er.cpp:39:43: error: 'type' in 'struct foo<1, 1, sequence<> >' does not name a type
  using t = typename foo<1, 1, extents<>>::type;
                                           ^

Вот вывод clang ++:

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

3 ответа

Решение

Это похоже на ошибку g++, потому что ясно foo<B, B, extents> более специализированный, чем foo<A, B, extents> (последние могут соответствовать чему-либо, что соответствует первому, но не наоборот), поэтому компилятор должен выбрать эту специализацию.

Как ты сам заметил, меняешься extents от псевдонима шаблона до шаблона класса, решает проблему.

Вопрос сводится к тому, правильно ли я понял, чтобы определить, является ли одна из следующих специализаций шаблона более специализированной, чем другая:

template <int A, int B, class Current>
struct foo;

template <int A, int B, int... Ts>
struct foo<A, B, extents<Ts...>>
{
    using type = int;
};

template <int B, int... Ts>
struct foo<B, B, extents<Ts...>>
{
    using type = int;
};

И ответ - да, для любой комбинации параметров, которые допускаются во второй специализации, такая же комбинация допускается в первой путем создания аргументов шаблона. A == B, С другой стороны, любая реализация первого шаблона специализации, на которой A != B не может быть совпадением для второй специализации, поэтому вторая является строго более специализированной, чем первая.

Я считаю, что g++ может быть правильным. Линия

using t = foo<1, 1, extents<>>::type

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

Раздел 14.8.2.5, параграф 4 стандарта C++ гласит:

В большинстве случаев типы, шаблоны и нетиповые значения, используемые для составления P, участвуют в выводе аргументов шаблона. То есть они могут использоваться для определения значения аргумента шаблона, и определенное таким образом значение должно соответствовать значениям, определенным в другом месте. Однако в определенных контекстах значение не участвует в выводе типа, а вместо этого использует значения аргументов шаблона, которые были либо выведены в другом месте, либо явно указаны. Если параметр шаблона используется только в не выводимых контекстах и ​​не указан явно, вывод аргумента шаблона завершается неудачно.

Неведуемые контексты:

x Спецификатор вложенного имени типа, указанного с помощью квалифицированного идентификатора.

...

Раздел 14.8.2.4, пункт 11 гласит:

В большинстве случаев все параметры шаблона должны иметь значения, чтобы вывод был успешным, но для целей частичного упорядочения параметр шаблона может оставаться без значения при условии, что он не используется в типах, используемых для частичного упорядочения. [Примечание. Параметр шаблона, используемый в невзаимодействующем контексте, считается использованным. —Конечная записка]

Таким образом, мы находимся в невыгруженном контексте, что означает, что все аргументы шаблона должны иметь значения. Таким образом, если extents<> не сможет определить тип, результат будет неоднозначным в соответствии со стандартом. Правдоподобно?

Другие вопросы по тегам