变量模板作为模板参数:演绎适用于GCC但不适用于Clang

psy*_*ill 22 c++ gcc clang variadic-templates c++11

在用GCC 4.7.2和Clang 3.1编译一些C++ 11代码时,我遇到了一个问题,Clang没有设法推断出GCC成功的模板参数.在更抽象的形式中,代码如下所示:

SRC/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;
}
Run Code Online (Sandbox Code Playgroud)

通过阅读§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
Run Code Online (Sandbox Code Playgroud)

为什么结果不同?是GCC马虎,Clang笨,我的代码是否包含未指定的行为或全部?

Ric*_*ith 7

Clang试图推断出这个调用的参数:

processVariadic(SecondContainer<Element>{});
Run Code Online (Sandbox Code Playgroud)

由于SecondContainer有一个默认模板参数,这相当于:

processVariadic(SecondContainer<Element, Element>{});
Run Code Online (Sandbox Code Playgroud)

因此,它使用P= Container<Element>A= 执行模板参数推导SecondContainer<Element, Element>.它可以立即推断出Container模板参数是SecondContainer.

接下来,它会考虑模板参数.由于参数类型已完全解析,因此Clang认为参数必须具有尽可能多的类型,否则推论不可能成功(它不会考虑默认参数).所以它标志着演绎失败.


那么,应该发生什么?用[temp.deduct.type]p8,

如果并且具有以下形式之一,则可以推导出模板类型参数T,模板模板参数TT或模板非类型参数: 其中[...] 表示模板参数列表,其中至少一个参数包含a ,表示模板参数列出至少一个参数包含一个并表示模板参数列表的地方,其中没有参数包含一个或一个.PA
[...]
TT<T>
TT<i>
TT<>
<T>T<i>i<>Ti

为了匹配模板参数,我们转向[temp.deduct.type]p9:

如果P具有包含<T>或的表单<i>,则将Pi相应模板参数列表的每个参数与相应模板参数列表P的对应参数Ai进行比较A.

这里有两件事需要注意.一个是这个规则没有说明如果列表PiAi不同长度会发生什么(在这种情况下是这样),并且常见的解释似乎是不检查不匹配的项目.另一个是不应该遵循这条规则,因为形式P不包含<T><i>(它只包含<>,因为它中没有模板参数).


因此,Clang拒绝此代码是错误的.我在r169475中修复了它.