Я пишу некоторый TMP-тяжелый код для g ++ (версия 4.8.1_1, Macports) и clang ++ (версия 3.3, Macports). В то время как g ++ отклоняет следующий листинг кода с НЕВЕРОЯТНАЯ ПУТЬ, clang ++ компилирует его Грейс а также пышность.
Вот листинг кода, сделанный только для тебя.
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 ++:
Спасибо за вашу помощь!
Это похоже на ошибку 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 гласит:
В большинстве случаев все параметры шаблона должны иметь значения, чтобы вывод был успешным, но для целей частичного упорядочения параметр шаблона может оставаться без значения при условии, что он не используется в типах, используемых для частичного упорядочения. [Примечание. Параметр шаблона, используемый в невзаимодействующем контексте, считается использованным. —Конечная записка]
Таким образом, мы находимся в невыгруженном контексте, что означает, что все аргументы шаблона должны иметь значения. Так что если экстенты<> Не удастся закрепить тип, тогда результат будет неоднозначным в соответствии со стандартом. Правдоподобно?