2012-11-22 47 views
21

在使用GCC 4.7.2和Clang 3.1编译一些C++ 11代码时,我碰到了一个问题,Clang没有设法推导出模板参数GCC成功。 在更抽象的形式,代码看起来是这样的:作为模板参数的可变参数模板:演绎适用于GCC,但不适用于Clang

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是否马虎,铿锵笨拙,我的代码是否包含未指定的行为或全部?

+0

我同意你的意见。我在C++ 11最终草稿中看到的所有内容都表明这应该起作用。 14.3.3.3尤其相关。 –

+0

你的例子缺少'typedef int Element;',对吧? – Quuxplusone

+0

不,在代码的开头我定义了一个名为Element的结构体。 – psyill

回答

7

锵试图推断论据此调用:

processVariadic(SecondContainer<Element>{}); 

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

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

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

接下来,它会考虑模板参数。由于参数类型已完全解析,Clang认为该参数必须具有多个类型,否则扣除不可能成功(它不考虑默认参数)。所以它标志着扣除失败。


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

模板类型参数T,一个模板的模板参数TT或模板非类型参数我可以如果PA具有下列形式中的一种来推断:
[...]
TT<T>
TT<i>
TT<>
其中[...] <T>代表模板参数列表,其中至少一个ARG包含T,<i>表示模板参数列表,其中至少一个参数包含i<>表示模板参数列表,其中参数不包含Ti

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

如果P有一个包含<T><i>一种形式,那么相应的模板参数列表P的每个参数Pi与比较相应的参数Ai的相应模板参数列表为A

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


所以,铿锵拒绝这个代码是错误的。我已经修复它在r169475

相关问题