考虑代码:
#include <tuple>
template<class... Args, class T>
T method(std::tuple<Args...>, Args..., T, ...) {
return T();
}
int main() {
method(std::make_tuple<int, float, double>(1, 1.0f, 1.0),
1, 1.0f, 1.0, 1);
}
g++
因为4.9编译它没有问题。另一方面,clang++
提供了错误:
main.cpp:9:5: error: no matching function for call to 'method'
method(std::make_tuple<int, float, double>(1, 1.0f, 1.0),
^~~~~~
main.cpp:4:3: note: candidate template ignored: couldn't infer template argument 'T'
T method(std::tuple<Args...>, Args..., T, ...) {
^
1 error generated.
哪种编译器是对的?
答案 0 :(得分:1)
代码格式不正确。两个编译器都无法正确编译:
#include <tuple>
template<class... Args, class T>
T method(std::tuple<Args...>, Args..., T) {
return T();
}
int main() {
method(std::make_tuple<int, float, double>(1, 1.0f, 1.0),
1, 1.0f, 1.0, 1);
}
我们确实从[temp.deduct.call]开始强调我的:
当一个函数参数包 出现在非推导的上下文(14.8.2.5)中,该参数包的类型从未推断。
Args...
出现在非推断的上下文中,因为来自[temp.deduct.type]:
非推断的上下文是:[...]一个函数参数包,它不会出现在 parameter-declaration-list 的末尾。
因此,根据标准的确切措辞,Args...
不得推断,因此您必须明确提供 - 在这种情况下仍然无法实现T
。
但在这种情况下,Args...
同时出现在推导出的和非推断的上下文中 - 应该没有任何内容阻止Args...
从{{1}中推断出来}参数为tuple
,然后简单地重复使用对可变参数的推导,然后在最后推导{int, float, double}
为T
。
但是这个提议的过程会与我们独立处理每个参数/参数对的典型演绎过程发生冲突。在这种情况下,int
上的扣除很大程度上取决于T
参数/参数对中Args...
的扣除。
如果您只是按顺序翻转:
tuple<>
然后两个编译器都编译它。
我不知道为什么gcc接受varargs的原始示例。这显然是错的。
此外,如果您翻转模板参数规范,以便您可以指定template <class... Args, class T>
T method(std::tuple<Args...>, T, Args...) { ... }
而不需要扣除,那么clang接受但gcc拒绝:
T
我希望这个格式正确 - template<class T, class... Args>
T method(std::tuple<Args...>, Args..., T) {
return T();
}
int main() {
method<int>(std::make_tuple<int>(1), 1, 1);
}
可以推断,而Args...
不一定是T
。因此,在我看来,“从未推断过”的措辞是值得怀疑的。