Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Return type deduction for in-class friend functions

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?

like image 670
TemplateRex Avatar asked Sep 21 '13 11:09

TemplateRex


People also ask

What should be the return type of friend function?

As we can see above, the friend function should be declared inside the class whose private and protected members are to be accessed. Let's breakdown the syntax: friend is a keyword to denote that this function is a friend function. returnType is the function's return type.

Can friend function be defined in class?

Friend functions can be defined (given a function body) inside class declarations. These functions are inline functions. Like member inline functions, they behave as though they were defined immediately after all class members have been seen, but before the class scope is closed (at the end of the class declaration).

How friend function can be used in two classes?

A friend function can be friendly to 2 or more classes. The friend function does not belong to any class, so it can be used to access private data of two or more classes as in the following example. The friend functions can serve, for example, to conduct operations between two different classes.


1 Answers

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.

like image 94
dyp Avatar answered Sep 22 '22 05:09

dyp