类内友元函数的返回类型推导 [英] Return type deduction for in-class friend functions

查看:41
本文介绍了类内友元函数的返回类型推导的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是一个关于 返回类型推导 未记录的类内友元函数(在两种情况下都使用 Clang 3.4 SVN 和 g++ 4.8.1 以及 std=c++1y)在链接的工作文件中

#include 结构A{国际a_;友元自动运算符==(A const& L, A const& R){返回 L.a_ == R.a_;//a_ 是 int 类型,所以应该返回 bool}};模板结构体B{国际b_;友元自动运算符==(B const& L, B const& R){返回 L.b_ == R.b_;//b_ 是 int 类型,所以应该返回 bool}};使用BI=B int ;int main(){std::cout <<(A{1} == A{2}) <<"
";//Clang 正常,g++ 错误std::cout <<(BI{1} == BI{2}) <<"
";//Clang 和 g++ 的错误}

实例.

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

解决方案

关于其他答案:我们正在明确处理 n3638 在这里,以及它是如何被纳入最近的 C++1y 草案的.

我正在使用 委员会的 github 存储库 中的 9514cc28,其中已经对 n3638 进行了一些(次要)修复/更改.

n3638 明确允许:

struct A {自动 f();//前向声明};自动 A::f() { 返回 42;}

而且,我们可以从 [dcl.spec.auto] 推断出,在指定此功能的地方,即使以下内容也是合法的:

struct A {自动 f();//前向声明};一个 x;自动 A::f() { 返回 42;}int main() { x.f();}

(但稍后会详细介绍)

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

另外,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@(..) 通常是允许的(这是从函数声明的组成为 TD,其中 T 的形式为 decl-specifier-seq,而 auto 是一个 类型说明符).

<小时>

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

[dcl.spec.auto]/1 说

<块引用>

autodecltype(auto) type-specifiers 指定稍后将被替换的占位符类型或者通过带有 trailing-return-type 的显式规范.

和/2

<块引用>

如果函数声明的返回类型包含占位符类型,则从函数体中的return语句推导出函数的返回类型(如果有).

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

<小时>

3.友元函数呢?

到目前为止,例子

结构体A{国际a_;友元自动运算符==(A const& L, A const& R);}自动运算符==(A const& L, A const& R){ 返回 L.a_ == R.a_;}

应该是格式良好的.现在有趣的部分是A的定义里面的朋友函数的定义,也就是

结构体A{国际a_;友元自动运算符==(A const& L, A const& R){ 返回 L.a_ == R.a_;}//允许吗?}

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

struct X{void foo() { m = 42;}国际米;};

注意 mfoo 中使用之前是如何不被声明的,但它是 X 的成员.

由此,我得出结论,即使

struct X{自动 foo() { 返回 m;}国际米;}

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

struct X{自动 foo() { 返回 X();}X() = 删除;};

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

<小时>

4.模板呢?

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

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

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

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

template结构A{int foo() { 返回 m;}国际米;};

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

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

<小时>

另见isocpp-forum 中的这个高度相关的讨论.

Here is a little experiment with return type deduction for in-class friend functions (using Clang 3.4 SVN and g++ 4.8.1 with std=c++1y in both cases) that is not documented in the linked working paper

#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}) << "
";    // OK for Clang, ERROR for g++
    std::cout << (BI{1} == BI{2}) << "
";  // ERROR for both Clang and g++
}

Live Example.

Question: is automatic return type deduction for in-class friend functions supported in C++14?

解决方案

With respect to the other answers: We're dealing explicitly with n3638 here, and how it's incorporated in the recent drafts of C++1y.

I'm using 9514cc28 from the commitee's github repository, which incorporates some (minor) fixes/changes to n3638 already.

n3638 allows explicitly:

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

And, as we can infer from [dcl.spec.auto], where this feature is specified, even the following will be legal:

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

A x;

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

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

(but more on this later)

This is fundamentally different from any trailing-return-type or dependent name lookup, as auto f(); is a preliminary declaration, similar to struct A;. It needs to be completed later on, before it is used (before the return type is required).

Additionally, the problems in the OP are related to internal compiler errors. The recent clang++3.4 trunk 192325 Debug+Asserts build fails to compile as an assertion fails while parsing the line return L.b_ == R.b_;. I have not checked with a recent version of g++ as of now.


Is the OP's example legal wrt to a n3638?

This is a bit tricky IMO. (I'm always referring to 9514cc28 in this section.)

1. Where is it allowed to use `auto`?

[dcl.spec.auto]

6    A program that uses auto or decltype(auto) in a context not explicitly allowed in this section is ill-formed.

2    The placeholder type can appear with a function declarator in the decl-specifier-seq, type-specifier-seq, conversion-function-id, or trailing-return-type, in any context where such a declarator is valid.

/5 also defines some contexts, but they're irrelevant here.

Therefore, auto func() and auto operator@(..) are generally allowed (this follows from the composition of a function declaration as T D, where T is of the form decl-specifier-seq, and auto is a type-specifier).


2. Is it allowed to write `auto func();`, i.e. a declaration that is not a definition?

[dcl.spec.auto]/1 says

The auto and decltype(auto) type-specifiers designate a placeholder type that will be replaced later, either by deduction from an initializer or by explicit specification with a trailing-return-type.

and /2

If the declared return type of the function contains a placeholder type, the return type of the function is deduced from return statements in the body of the function, if any.

Although it doesn't explicitly allow a declaration like auto f(); for a function (that is, a declaration without definition), it is clear from n3638 and [dcl.spec.auto]/11 that it is intended to be allowed, and not explicitly forbidden.


3. What about friend functions?

So far, the example

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_; }

should be well-formed. The interesting part now is the definition of the friend function inside the definition of A, that is

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

In my opinion, it is allowed. To support this, I'll cite name lookup. The name lookup inside the definition of functions defined in a friend function declaration follows the name lookup of member functions as per [basic.lookup.unqual]/9. /8 of the same section specifies unqualified lookup for names used inside member function bodies. One of the ways a name can be declared to be used is that it "shall be a member of class X or be a member of a base class of X (10.2)". This allows the widely known

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

Note how m isn't declared before its use in foo, but it's a member of X.

From this, I conclude that even

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

is allowed. This is supported by clang++3.4 trunk 192325. Name lookup requires to interpret this function only after the struct has been completed, also consider:

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

Similarly, the body of friend functions defined inside a class can only be interpreted once the class is complete.


4. What about templates?

Specifically, what about friend auto some_function(B const& L) { return L.b_; }?

First, the injected-class-name B is equivalent to B<T>, see [temp.local]/1. It refers to the current instantiation ([temp.dep.type]/1).

The id-expression L.b_ refers to a member of the current instantiation (/4). It is also a dependent member of the current instantiation -- this is an addition made after C++11, see DR1471, and I don't know what to think about it: [temp.dep.expr]/5 states this id-expression is not type-dependent, and as far as I see [temp.dep.constexpr] doesn't say it's value-dependent.

If the name in L.b_ was not dependent, name lookup would follow the "usual name lookup" rules per [temp.nondep]. Else, this'll be fun (dependent name lookup is not very well specified), but considering that

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

is accepted by most compilers as well, I think the version with auto should be valid, too.

There's also a section about friends of templates in [temp.friend], but IMO it doesn't shed light on the name lookup here.


Also see this highly relevant discussion in the isocpp-forum.

这篇关于类内友元函数的返回类型推导的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆