【发布时间】:2022-07-08 00:40:50
【问题描述】:
考虑以下示例:
#include
template
struct S
{
friend int Func(T) // decl-1
{
return V;
}
};
struct U
{
friend int Func(U); // decl-2
};
template struct S; // spec-1
int main()
{
std::cout << Func(U{}) << std::endl; // Compiles and prints 42
}
我的理解是表达式
Func(U{})
会导致函数
Func
的非限定名称查找,并通过ADL找到声明
decl-2
。
但是,这不是一个可行的重载解析候选函数(因为它没有定义),因此编译器选择声明
decl-1
。
误解,并且与问题无关,请参阅@LanguageLawyer 的评论
我的问题是标准中的哪些规则允许编译器使用来自专业化 spec-1 的模板参数来实例化包含 decl- 的类模板1 .
搜索 cppreference ,我发现唯一似乎适用的规则是指函数模板的重载解析,并引用:
对于函数模板,执行模板参数推导和检查任何显式模板参数,以找到可以在这种情况下使用的模板参数值(如果有):
- 如果两者都成功,则模板参数用于合成相应函数模板特化的声明,将其添加到候选集中,并且此类特化被视为非模板函数,除非在决胜局中另有规定规则;
- 如果参数推导失败或合成的函数模板特化格式不正确,则不会将此类函数添加到候选集中。
来源: https://en.cppreference.com/w/cpp/language/overload_resolution#Details
decl-1
是否被视为用于重载决议的函数模板?编译器是否使用
spec-1
合成声明
template int Func<U, 42>(U)
(可能是通过模板参数推导)?或者这里还有其他什么东西在起作用?
编辑:
我可能还有一个误解是
spec-1
到底是什么,我目前的理解是它是类模板的显式特化的声明
S
是一个不完整的类型,如果可能,请澄清这是否正确。
-
这不是重载解析的可行候选函数(因为它没有定义) 什么时候“未定义”成为重载解析标准?如何区分“未定义”和“在另一个 TU 中定义”?
-
来自 spec-1 的 decl-1 只是 decl-2 的定义。它们声明了相同的函数。
-
@LanguageLawyer 谢谢你,在过去的几个小时里,我一直在回顾我对语言的理解,确实 decl-2 只是一些函数
Func
的声明,说明它没有定义与重载决议无关(实际上,它可能在别处定义)。但后来我想我可能对 spec-1 到底是什么有一些误解,我将其理解为 S 的不完整显式专业化的声明,但从你的评论来看,我猜这可能不太正确。我将编辑问题以清除错误的假设并反映这些疑虑。 -
spec-1 是一个显式实例化。
template <>
介绍了明确的专业化。 timsong-cpp.github.io/cppwp/n4861/… timsong-cpp.github.io/cppwp/n4861/… -
@LanguageLawyer 好的,哇,感谢您为我解决这个问题,一直以来(可能是因为不经常看到它)我认为这只是写
template <>
的简写,但从我现在明白了, spec-1 似乎与template <> struct S<U, 42> { friend int Func(U) { return 42; } }
没有什么不同。如果您将其发布为答案,我将很乐意接受。