类模板中声明的友元函数的模板参数的扣除

分享于2022年07月17日 argument-dependent-lookup c++ language-lawyer overload-resolution template-argument-deduction 问答
【问题标题】:类模板中声明的友元函数的模板参数的扣除(Deduction of template arguments for friend function declared in class template)
【发布时间】: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; } } 没有什么不同。如果您将其发布为答案,我将很乐意接受。

【解决方案1】:

另一个我可能有的误解是 spec-1 到底是什么,我目前的理解是它是类模板 S 的显式特化声明为不完整类型,如果可能,请澄清这是否正确。< /p>

这是不正确的。

template struct S; // spec-1

根本不是专业,而是 explicit instantiation

它只是告诉编译器发出带有参数 T=U V=42 S 的相同实例化,如果您在任何其他上下文中引用 S<U, 24> 就会发出这些实例化。

作为不完整类型的特化看起来像

template <> struct S;