clang 不使用可变参数推断可变参数模板函数中的模板参数

clang does not infer template argument in variadic template function with varargs

考虑代码:

#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.

哪个编译器是正确的?

代码格式错误。两个编译器都正确地编译失败​​:

#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],强调我的:

When a function parameter pack appears in a non-deduced context (14.8.2.5), the type of that parameter pack is never deduced.

Args... 出现在非推导上下文中,因为来自 [temp.deduct.type]:

The non-deduced contexts are: [...] A function parameter pack that does not occur at the end of the parameter-declaration-list.

因此,根据标准的确切措辞,不应推导出 Args...,因此您必须明确提供它 - 在这种情况下,这仍然不可能归因于 T

但在这种情况下,Args... 出现在推导的 非推导的上下文中 - 应该没有什么可以阻止 Args...tuple 参数为 {int, float, double},然后简单地重新使用可变参数的推导,最后将 T 推导为 int

但是 这个提议的过程会与我们独立处理每个 parameter/argument 对的典型推导过程冲突。在这种情况下,T 的推导很大程度上取决于 tuple<> parameter/argument 对 Args... 的推导。

如果您简单地翻转顺序:

template <class... Args, class T>
T method(std::tuple<Args...>, T, Args...) { ... }

然后两个编译器都会编译它。

我不知道为什么 gcc 接受带有可变参数的原始示例。这显然是错误的。

此外,如果您翻转模板参数规范以便您可以指定 T 而无需扣除,则 clang 接受但 gcc 拒绝:

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 不一定是。所以 "never deduced" 上的措辞在我看来是有问题的。