【问题标题】:Return type deduction for in-class friend functions类内友元函数的返回类型推导
【发布时间】:2013-09-26 17:46:12
【问题描述】:

这是一个使用return type deduction 进行课堂友元函数的小实验(在两种情况下都使用 Clang 3.4 SVN 和 g++ 4.8.1 和 std=c++1y),链接的工作文件中没有记录

#include <iostream>

struct A
{
    int a_;
    friend auto operator==(A const& L, A const& R) 
    { 
        return L.a_ == R.a_; // a_ is of type int, so should return bool
    }
};

template<class T>
struct B
{
    int b_;
    friend auto operator==(B const& L, B const& R) 
    { 
        return L.b_ == R.b_; // b_ is of type int, so should return bool
    }
};

using BI = B<int>;

int main()
{
    std::cout << (A{1} == A{2}) << "\n";    // OK for Clang, ERROR for g++
    std::cout << (BI{1} == BI{2}) << "\n";  // ERROR for both Clang and g++
}

Live Example.

问题:C++14 支持类内友元函数的自动返回类型推导吗?

【问题讨论】:

  • 我不认为问题出在模板中定义的朋友函数Live example 有“即使函数体包含带有非类型相关操作数的返回语句,在定义被实例化时,在其声明类型中带有占位符的函数模板的返回类型推导也会发生”但我找不到关于类模板成员或类模板中定义的友元函数的任何信息。
  • 因为有 [temp.friend]/4 “当一个函数在类模板中的友元函数声明中定义时,该函数在该函数被 odr-used 时实例化。”,也许是措辞在 [dcl.spec.auto]/12 中应该是“函数模板的返回类型推导,类模板的成员函数和类模板中定义的友元函数”。
  • @DyP tnx 获取这些报价。我上面链接的工作文件尚未反映在当前的标准草案中,但我发现该文件阅读起来相当混乱,并且没有类似于上述代码的示例。
  • 您的示例具有误导性,错误是定义函数的位置,而不是您尝试调用它们的位置。您可以在main() 中注释掉这两行,但它仍然失败(clang trunk 甚至在operator==(const B&amp;, const B&amp;) 上崩溃。)

标签: c++ auto c++14 return-type-deduction


【解决方案1】:

关于其他答案:我们在这里明确处理n3638,以及它是如何合并到最近的 C++1y 草案中的。

我正在使用来自commitee's github repository 的 9514cc28,它已经包含对 n3638 的一些(次要)修复/更改。

n3638 明确允许:

struct A {
  auto f(); // forward declaration
};
auto A::f() { return 42; }

而且,正如我们可以从 [dcl.spec.auto] 中推断的那样,在指定此功能的地方,甚至以下内容也是合法的:

struct A {
  auto f(); // forward declaration
};

A x;

auto A::f() { return 42; }

int main() { x.f(); }

(稍后会详细介绍)

​​>

这与任何 trailing-return-type 或从属名称查找根本不同,因为auto f(); 是一个初步声明,类似于struct A;。它需要稍后完成,在使用之前(在需要返回类型之前)。

此外,OP 中的问题与内部编译器错误有关。最近的 clang++3.4 主干 192325 Debug+Asserts 编译失败,因为在解析行 return L.b_ == R.b_; 时断言失败。到目前为止,我还没有检查过最新版本的 g++。


OP 的示例对 n3638 是否合法?

这对 IMO 来说有点棘手。 (在本节中,我总是指 9514cc28。)

1。什么地方可以使用`auto`?

[dcl.spec.auto]

6    在本节未明确允许的上下文中使用 autodecltype(auto) 的程序是格式错误的。

2    占位符类型可以在 decl-specifier-seqtype-specifier-seqconversion-function-id 或 trailing-return-type,在此类声明符有效的任何上下文中。

/5 还定义了一些上下文,但它们在这里无关紧要。

因此,auto func()auto operator@(..) 通常是允许的(这源于 T D 的函数声明的组合,其中 T 的形式为 decl-specifier-seq , 而auto 是一个类型说明符)。


2。是否允许写`auto func();`,即不是定义的声明?

[dcl.spec.auto]/1 说

autodecltype(auto) type-specifiers 指定一个占位符类型,稍后将被替换,可以通过初始化器的推导或通过带有 trailing-return- 的显式规范输入

和/2

如果函数声明的返回类型包含占位符类型,则函数的返回类型是从函数体中的return语句推导出来的。

虽然它明确 不允许函数像 auto f(); 这样声明(即没有定义的声明),但从 n3638 和 [dcl.spec.auto]/ 中可以清楚地看到11 它是被允许的,而不是明确禁止的。


3。朋友功能呢?

到目前为止,示例

struct A
{
    int a_;
    friend auto operator==(A const& L, A const& R);
}

auto operator==(A const& L, A const& R)
{ return L.a_ == R.a_; }

应该格式正确。现在有趣的部分是A的定义里面的friend函数的定义,也就是

struct A
{
    int a_;
    friend auto operator==(A const& L, A const& R)
    { return L.a_ == R.a_; } // allowed?
}

在我看来,这是允许的。为了支持这一点,我将引用名称查找。在友元函数声明中定义的函数定义中的名称查找遵循 [basic.lookup.unqual]/9 中的成员函数的名称查找。同一节的 /8 指定了对成员函数体内使用的名称的非限定查找。可以声明使用名称的一种方式是,它“应为X 类的成员或X (10.2) 基类的成员”。这使得广为人知的

struct X
{
    void foo() { m = 42; }
    int m;
};

注意mfoo 中使用之前未声明,但它是X 的成员。

由此得出结论,即使

struct X
{
    auto foo() { return m; }
    int m;
}

是允许的。这是由 clang++3.4 主干 192325 支持的。 名称查找需要在 struct 完成后解释此函数,还可以考虑:

struct X
{
    auto foo() { return X(); }
    X() = delete;
};

同样,类中定义的友元函数体只能在类完成后才能解释。


4。模板呢?

具体来说,friend auto some_function(B const&amp; L) { return L.b_; }呢?

首先,injected-class-name B 等价于 B&lt;T&gt;,参见 [temp.local]/1。它指的是当前实例化 ([temp.dep.type]/1)。

id 表达式 L.b_ 指的是当前实例化的成员 (/4)。它也是当前实例化的依赖成员——这是在 C++11 之后添加的,参见 DR1471,我不知道该怎么想:[temp. dep.expr]/5 声明这个 id-expressionnot 类型相关的,据我所知 [temp.dep.constexpr] 并没有说它的价值-依赖。

如果L.b_ 中的名称不依赖,则名称查找将遵循每个 [temp.nondep] 的“常用名称查找”规则。否则,这会很有趣(依赖名称查找没有很好地指定),但考虑到这一点

template<class T>
struct A
{
    int foo() { return m; }
    int m;
};

也被大多数编译器所接受,我认为带有auto 的版本也应该是有效的。

[temp.friend] 中还有一个关于模板好友的部分,但 IMO 并没有说明此处的名称查找。


另见this highly relevant discussion in the isocpp-forum

【讨论】:

  • 如果可以的话,+10,但是我需要处理一些很棒的东西。非常感谢!
  • IIRC,类模板中定义的友元函数是封闭命名空间中的常规函数​​,只能通过ADL找到。由于您提到了与实例化相关的一些技术细节,我想知道这里是否存在两阶段名称查找的干扰。具体来说,究竟何时推导出返回类型?
  • @TemplateRex [class.friend]/7 "在类中定义的 friend 函数在定义它的类的(词法)范围内。" - 这会影响不合格的查找(加上它隐含的inline)。 “具体来说,究竟是什么时候推导出来的返回类型?”好问题;我已经在 cmets 中对您的问题说过,我找不到对此的明确陈述。但是,正如链接的 isocpp 讨论中也出现的那样,它不能在封闭类完成之前。
  • 好的,我想标准和当前的 QoI 都需要考虑,也许在接下来的几个月里会变得清晰。我将在周末结束的远程活动中接受答案,即会有更好的事情出现。
猜你喜欢
  • 2018-07-12
  • 2015-03-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-02-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多