在使用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笨,我的代码是否包含未指明的行为或所有这些行为?
答案 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
,
如果
T
和TT
具有以下其中一项,则可以推断出模板类型参数P
,模板模板参数A
或模板非类型参数i形式:
[...]
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>
(它只包含<>
,因为没有模板其中的参数)。
因此,Clang拒绝此代码是错误的。我已将其修复为r169475。