30

这是一个关于类内朋友函数的返回类型推导的小实验(在两种情况下都使用 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++
}

活生生的例子

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

4

1 回答 1

11

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

我正在使用来自提交人的 github 存储库的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(); }

(但稍后会详细介绍)

这与任何尾随返回类型或从属名称查找根本不同,auto f();初步声明也是如此,类似于struct A;. 它需要稍后在使用之前完成(在需要返回类型之前)。

此外,OP 中的问题与内部编译器错误有关。最近的 clang++3.4 主干 192325 Debug+Asserts build 编译失败,因为解析行时断言失败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-idtrailing-return-type中,在这样的声明符有效的任何上下文中。

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

因此,auto func()andauto operator@(..)通常是允许的(这来自于函数声明的组合 as T D,其中Tdecl-specifier-seq的形式,并且auto是一个类型说明符)。


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

[dcl.spec.auto]/1 说

autodecltype(auto) 类型说明符指定稍后将被替换的占位符类型,或者通过从初始化程序中推导,或者通过带有尾随返回类型的显式说明。

和 /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,即

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

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

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

请注意如何m在使用之前未声明 in foo,但它是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& L) { return L.b_; }办?

首先,注入的类名 B等价于B<T>,请参见 [temp.local]/1。它指的是当前实例化([temp.dep.type]/1)。

id 表达式 L.b_引用当前实例化的成员( /4)。它也是当前实例化的依赖成员——这是在 C++11 之后添加的,请参阅DR1471,我不知道该怎么想:[temp.dep.expr]/5 states this id -expression不依赖于类型,据我所知 [temp.dep.constexpr] 并没有说它依赖于值。

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

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

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

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


还可以在 isocpp-forum 中查看这个高度相关的讨论

于 2013-10-11T19:41:24.507 回答