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

时间:2012-11-22 23:10:56

标签: c++ gcc c++11 clang variadic-templates

在使用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;
}

通过阅读§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笨,我的代码是否包含未指明的行为或所有这些行为?

1 个答案:

答案 0 :(得分:7)

Clang试图推断出这个调用的论据:

processVariadic(SecondContainer<Element>{});

由于SecondContainer具有默认模板参数,因此相当于:

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

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

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


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

的话来说
  

如果TTT具有以下其中一项,则可以推断出模板类型参数P,模板模板参数A或模板非类型参数i形式:
    [...]
    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