使用显式模板参数列表和 [temp.arg.explicit]/3 的函数调用的演绎失败
Deduction failure of function call with explicit template argument list and [temp.arg.explicit]/3
[temp.arg.explicit]/3 的 C++17 标准(最终草案)说到了使用显式指定的模板参数列表推导函数模板参数
:在推导完成但失败的上下文中,或者 [...],如果指定了模板参数列表,并且它与任何默认模板参数一起标识单个函数模板专用化,则 template-id 是函数模板专用化的左值。
这如何应用于参数包?
考虑
template<typename...>
struct S {
S(int) {}
};
template<typename... A>
void f(S<A...>) {}
int main() {
f<int>(0);
}
这在 MSVC 上编译,但不在 GCC 和 Clang 上编译,参见 godbolt。我的直觉也认为它应该失败,因为演绎会失败,但上面的引用似乎暗示即使演绎失败,因为f<int>
(在我的理解中)唯一标识模板专用化,f<int>
应该考虑引用该专用化,然后调用它,没有重载解决方案,这将起作用, 隐式将0
转换为S<int>
。
我对报价的理解有什么问题,或者 MSVC 确实正确?
请注意,如果我们尝试调用f<>(0);
(我想应该按照上面的考虑工作),所有三个编译器都拒绝编译。
与该问题相关的还有 [temp.arg.explicit]/6,它告诉我们函数参数的隐式转换(如上所示)是
如果参数类型不包含参与模板参数推导的模板参数。[ 注意:如果明确指定了模板参数,则模板参数不参与模板参数推导。 [...]]
所以,现在的问题是A...
是否参与模板参数推导。(在这一点上,我想指出,如果我们用一个模板参数替换参数包,OPs 代码也会在 gcc/clang 下编译,因为它是显式指定的)。
有人可能会争辩说,A...
是明确规定的,因此不参与演绎。但我认为一个是错误的。[temp.arg.explicit]/9 告诉我们,演绎可以扩展显式指定的模板参数列表。因此,f<int>(S<int, char>{0});
是有效的,A...
被推导出为int, char
。所以在这种情况下,A...
肯定会参与扣除。但是,由于此调用仅与参数上的调用不同,因此推导也必须在调用中进行。
换句话说,f<int>(0);
也可能意味着调用f<int, char>
,因此,它不指定单个函数模板规范。
这无关紧要。没有"没有重载解决"的函数调用。CWG2092清楚地表明了这一点。
[temp.over]/1 控件(为便于阅读而分开;强调我的):
写入对函数或函数模板名称的调用时 (显式或隐式使用运算符表示法),模板 参数推导([temp.deduc])和检查任何明确的 为每个函数执行模板参数 ([temp.arg]) 模板以查找可以是 与该函数模板一起使用以实例化函数模板 可以使用调用参数调用的专用化。
对于每个函数模板,如果参数推导和检查成功,模板参数s(推导和/或显式)用于 合成单个函数模板的声明 添加到候选函数集的专业化 用于重载解析。
如果,对于给定的函数模板, 参数推导失败或合成函数模板失败 专业化格式不正确,没有这样的功能添加到 该模板的候选函数集。全套 候选函数包括所有合成声明和所有 同名的非模板重载函数。这 合成声明被视为与 过载解析的其余部分,除非在 [过度.匹配.最佳]。
有一个开放的核心语言问题(问题 2055:显式指定的非推导参数包)与此情况密切相关。
根据我对这个问题中隐含的内容的理解,目的是编译器应该表现为 MSVC,但据说标准不够清晰。
- 如何在 C 中正确使用 libiconv 使其不会报告"Arg list too long"?
- 将 N-arg 函数包装到另一个函数中
- 使用显式模板参数列表和 [temp.arg.explicit]/3 的函数调用的演绎失败
- 为什么不接受具有默认分配参数的函数作为 0-arg 生成器?
- C++重载解析总是诉诸别名 arg. 而不是其他人作为对所有人都有效的 arg.
- 区分两个零arg构造函数的惯用方法
- 统一初始化是隐式发生的,即使 int 强制转换运算符是使用 explicit 关键字声明的.原因是什么?
- 将 mnist 客户端重写为 c++(arg[0] 不是矩阵)
- 在现代 c++ 中,有什么好方法可以获取 arg 类型列表表单函数指针吗?
- 书籍示例在类声明中使用"explicit"关键字。无效吗?
- 如何使用 z3 中的 arg() 函数
- 我如何使用explicit模板实例化用于类定义中定义的模板成员函数
- 为什么有必要将"this"指针作为"arg"参数传递给pthread_create
- 如何在数组中使用结构化绑定作为ARG传递给某些函数
- QSqlQuery 绑定值与 BindValues 与 QString.arg() 是否存在性能差异
- 在C 中,您可以创建一个对象作为arg function
- 外部模板'inconsistent explicit instantiations'
- execve()的envp arg arm中的ld_library_path即使调用setuid part删除了其特权,
- C 预期字符串在ARG之前
- 传递variadic arg to模板函数时的汇编误差