Return type deduction for in-class friend functions

后端 未结 1 810
迷失自我
迷失自我 2021-01-01 09:55

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 no

相关标签:
1条回答
  • 2021-01-01 10:36

    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.

    0 讨论(0)
提交回复
热议问题