Класс специализации шаблона приоритет / неоднозначность

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

template <typename ... Args>
struct A {};

template <template <typename...> class Z, typename T>
struct test;

template <template <typename...> class Z, typename T>
struct test<Z, Z<T>> {
    static void foo() {
        std::cout << "I'm more specialized than the variadic spec, hehe!" << std::endl;
    }
};

template <template <typename...> class Z, typename T, typename ... Args>
struct test<Z, Z<T, Args...>> {
    static void foo() {
        std::cout << "I'm variadic!" << std::endl;
    }
};

int main() {
    test<A, A<int>>::foo();
}

Под gcc выдает ошибку, потому что считает обе специализации одинаково специализированными при попытке создать экземплярtest<A, A<int>>:

main.cpp: In function 'int main()':

main.cpp:25:24: error: ambiguous template instantiation for 'struct test<A, A<int> >'

         test<A, A<int>>::foo();

                        ^~

main.cpp:11:12: note: candidates are: template<template<class ...> class Z, class T> struct test<Z, Z<T> > [with Z = A; T = int]

     struct test<Z, Z<T>> {

            ^~~~~~~~~~~~~

main.cpp:18:12: note:                 template<template<class ...> class Z, class T, class ... Args> struct test<Z, Z<T, Args ...> > [with Z = A; T = int; Args = {}]

     struct test<Z, Z<T, Args...>> {

Однако clang считает первую специализацию «более специализированной» (посредством частичного упорядочения: см. Следующий раздел), так как она хорошо компилируется и печатает:

Хе-хе, я более специализированный, чем специфика вариа!

A Live Демо можно найти на Колиру. Я также попытался использовать версию GCC HEAD и получил те же ошибки.

Мой вопрос здесь следующий: так как эти два известных компилятора ведут себя по-разному, какой из них правильный, и является ли этот фрагмент кода правильным C ++?

Стандартная интерпретация C ++ 14 текущий проект)

Из разделов §14.5.5.1 и $ 14.5.5.2 стандартного черновика C ++ 14 запускается частичное упорядочение, чтобы определить, какую специализацию следует выбрать:

(1.2) - Если найдено более одной подходящей специализации, правила частичного порядка (14.5.5.2) используются для определения того, является ли одна из специализаций более специализированной, чем другие. Если ни одна из специализаций не является более специализированной, чем все другие соответствующие специализации, то использование шаблона класса является неоднозначным, и программа является плохо сформированной.

Теперь согласно §14.5.5.2 специализации шаблонов классов преобразуются в шаблоны функций посредством этой процедуры:

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

(1.1) - первый шаблон функции имеет те же параметры шаблона, что и первая частичная специализация, и имеет единственный параметр функции, тип которого является специализацией шаблона класса с аргументами шаблона первой частичной специализации, и

(1.2) - второй шаблон функции имеет те же параметры шаблона, что и вторая частичная специализация, и имеет единственный параметр функции, тип которого является специализацией шаблона класса с аргументами шаблона второй частичной специализации.

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

template <typename T>
void foo(T const&) {
    std::cout << "Generic template\n";
}

template <template <typename ...> class Z, typename T>
void foo(Z<T> const&) {
    std::cout << "Z<T>: most specialized overload for foo\n";
}

template <template <typename ...> class Z, typename T, typename ... Args>
void foo(Z<T, Args...> const&) {
    std::cout << "Z<T, Args...>: variadic overload\n";
}

Теперь пытаюсь использовать это так:

template <typename ... Args>
struct A {};

int main() {
    A<int> a;
    foo(a);
}

приводит к ошибке компиляции [неоднозначный вызов] в clang и gcc: живое демо. Я ожидал, что у clang будет, по крайней мере, поведение, соответствующее случаю шаблона класс

Затем, это моя интерпретация стандарта (которым я, похоже, делюсь с @Danh), поэтому на данный момент нам нужен Язык адвокат чтобы подтвердить это.

Заметка Я немного просмотрел трекер ошибок LLVM и не смог найти тикет для поведения, наблюдаемого при перегрузке шаблонов функций в этом вопросе.

Ответы на вопрос(1)

Ваш ответ на вопрос