Подтвердить что ты не робот

Шаблон Variadic в качестве параметра шаблона: вычет работает с GCC, но не с Clang

При компиляции некоторого кода С++ 11 как с GCC 4.7.2, так и с Clang 3.1 я столкнулся с проблемой, когда Clang не удалось вывести аргумент шаблона, где GCC преуспевает. В более абстрактной форме код выглядит следующим образом:

ЦСИ/test.cc:

struct Element {
};

template <typename T>
struct FirstContainer {
};

template <typename T, typename U = Element>
struct SecondContainer {
};

template <template <typename> class Container>
void processOrdinary(Container<Element> /*elements*/) {
}

template <template <typename, typename> class Container>
void processOrdinary(Container<Element, Element> /*elements*/) {
}

template <template <typename, typename...> class Container>
void processVariadic(Container<Element> /*elements*/) {
}

int main() {
  // This function instantiation works in both GCC and Clang.
  processOrdinary(FirstContainer<Element>{});
  // This function instantiation works in both GCC and Clang.
  processOrdinary(SecondContainer<Element>{});
  // This function instantiation works in both GCC and Clang.
  processVariadic(FirstContainer<Element>{});
  // This function instantiation works in both GCC and Clang.
  processVariadic<SecondContainer>(SecondContainer<Element>{});
  // This function instantiation works in GCC but not in Clang.
  processVariadic(SecondContainer<Element>{});
  return 0;
}

Из чтения примеров в §14.3.3 и спецификаций в §14.8.2 стандарта я считаю, что вычет должен работать, но я не могу сказать точно. Это результат, который я получаю из здания:

mkdir -p build-gcc/
g++ -std=c++0x -W -Wall -Wextra -Weffc++ -pedantic -c -o build-gcc/test.o src/test.cc
g++  -o build-gcc/test build-gcc/test.o
mkdir -p build-clang/
clang++ -std=c++11 -Weverything -Wno-c++98-compat -c -o build-clang/test.o src/test.cc
src/test.cc:34:3: error: no matching function for call to 'processVariadic'
  processVariadic(SecondContainer<Element>{});
  ^~~~~~~~~~~~~~~
src/test.cc:21:6: note: candidate template ignored: failed template argument deduction
void processVariadic(Container<Element> /*elements*/) {
     ^
1 error generated.
make: *** [build-clang/test.o] Fel 1

Почему результаты отличаются? Является ли GCC неаккуратным, Clang dumb, содержит ли мой код неуказанное поведение или все из них?

4b9b3361

Ответ 1

Clang пытается вывести аргументы для этого вызова:

processVariadic(SecondContainer<Element>{});

Так как SecondContainer имеет аргумент шаблона по умолчанию, это эквивалентно:

processVariadic(SecondContainer<Element, Element>{});

Таким образом, он выполняет вывод аргумента шаблона с помощью P= Container<Element> и A= SecondContainer<Element, Element>. Он может сразу вывести, что параметр шаблона Container SecondContainer.

Далее, он рассматривает аргументы шаблона. Поскольку тип аргумента полностью разрешен, Кланг считает, что параметр должен иметь столько типов, или вычет не может быть успешным (он не принимает аргументы по умолчанию). Таким образом, это означает отказ от вычета.


Итак, что должно произойти? По словам [temp.deduct.type]p8,

Аргумент типа шаблона T, аргумент шаблона шаблона TT или аргумент шаблона non-type я можно вывести, если P и A имеют одну из следующих форм:
  [...]
  TT<T>
  TT<i>
  TT<>
где [...] <T> представляет списки шаблонов шаблонов, где хотя бы один аргумент содержит T, <i> представляет список аргументов шаблона, где хотя бы один аргумент содержит строки i и <> представляет список аргументов шаблонов, где ни один аргумент не содержит T или i.

Чтобы сопоставить аргументы шаблона, переходим к [temp.deduct.type]p9:

Если P имеет форму, содержащую <T> или <i>, то каждый аргумент Pi соответствующего списка аргументов шаблона P сравнивается с соответствующим аргументом Ai соответствующего списка аргументов шаблона A.

Здесь есть две вещи. Во-первых, это правило не говорит, что произойдет, если в списке Pi и Ai есть разные длины (как и в этом случае), а общая интерпретация, по-видимому, заключается в том, что несоответствующие элементы не рассматриваются. Другое заключается в том, что это правило не следует соблюдать в любом случае, так как форма P не содержит <T> или <i> (она просто содержит <>, потому что в нем нет параметров шаблона).


Итак, Клэн ошибался, чтобы отклонить этот код. Я исправил его в r169475.