类内朋友函数的返回类型扣除 [英] Return type deduction for in-class friend functions

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

问题描述

这里是一个小小的实验, 返回类型扣除 (在这两种情况下使用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_的类型为int,所以应该返回bool
}
};

template< class T>
struct B
{
int b_;
friend auto operator ==(B const& L,B const& R)
{
return L.b_ == R.b_; // b_的类型为int,所以应该返回bool
}
};

使用BI = B< int> ;;

int main()
{
std :: cout< (A {1} == A {2})< \\\
; // OK for Clang,ERROR for g ++
std :: cout<< (BI {1} == BI {2})< \\\
; // Clang和g ++的错误
}

实例



问题:是在C ++ 14支持的类中友好函数的自动返回类型推导?

解决方案

答案:我们正在处理 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 trunk 192325 Debug + Asserts构建无法编译,因为断言失败,同时解析 return L.b_ == R.b _; 。我还没有检查过现在的g ++的最新版本。






OP的示例是合法的wrt到n3638吗?



这是一个有点棘手的IMO。 (我总是指本节中的9514cc28。)



1。在哪里允许使用`auto`?



[dcl.spec.auto]


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



2  占位符类型可以在 decl-specifier-seq type-specifier-seq conversion-function-id trailing-return-type


/ 5



因此, auto func() auto operator @(..)通常是允许的(这可以从 TD 的函数声明构成,其中 T 的格式为 decl-specifier-seq auto

)。






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



[dcl.spec.auto] / 1说


auto decltype(auto) 类型说明符指定稍后将替换的占位符类型,通过从初始化器中扣除或通过使用 trailing-return-type

>

和/ 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& 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?
}

在我看来,它是允许的。为了支持这一点,我将引用名称查找。在friend函数声明中定义的函数的定义中的名称查找跟在根据[basic.lookup.unqual] / 9的成员函数的名称查找之后。 / 8的同一部分指定在成员函数体内使用的名称的非限定查找。名称可以被声明为使用的方式之一是它将是 X 类的成员,或者是 X (10.2)。这允许广为人知的

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

请注意 m 它在 foo 中的使用,但它是 X 的成员。



由此,我得出结论,即使

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

这由clang ++ 3.4 trunk 192325支持。
名称查找需要在 struct 完成后解释此函数,还要考虑:

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

类似地,类中定义的友元函数主体只能在类完成时解释。






4。关于模板?



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



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



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



如果 L.b _ 中的名称不相关,则名称查找将遵循[temp.nondep]的通用名称查找规则。否则,这将是有趣的(依赖名称查找不是很好地指定),但考虑

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

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



在[temp.friend]中还有一个关于模板朋友的部分,






另请参阅 isocpp论坛中的高度相关的讨论。


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}) << "\n";    // OK for Clang, ERROR for g++
    std::cout << (BI{1} == BI{2}) << "\n";  // 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天全站免登陆