22

在使用 GCC 4.7.2 和 Clang 3.1 编译一些 C++11 代码时,我遇到了一个问题,即 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 愚蠢,我的代码是否包含未指定的行为或全部?

4

1 回答 1

7

Clang 正在尝试推断此调用的参数:

processVariadic(SecondContainer<Element>{});

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

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

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

接下来,它考虑模板参数。由于参数类型是完全解析的,Clang 认为参数必须有尽可能多的类型,否则推导不可能成功(它不考虑默认参数)。因此,它标志着扣除失败。


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

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

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

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

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


So, Clang was wrong to reject this code. I've fixed it in r169475.

于 2012-12-06T06:57:37.680 回答