Why would a struct need a friend function?

后端 未结 3 1322
刺人心
刺人心 2020-12-21 19:01

I\'m trying to build a program whose source I downloaded from the internet. When I try to compile it, I get the error message

friend declaration specifying          


        
3条回答
  •  时光说笑
    2020-12-21 19:35

    It took a while until I got it but finally I found out.

    Thereby, I ignore the mis-leading title (which has already been answered by user463035818 IMHO sufficiently) and concentrate on

    friend declaration specifying a default argument must be the only declaration

    which raised my attention.

    Therefore, I don't repeat what has been said about friend and access to public members because I think this error deals with a different issue.

    First I tried example of OP on coliru (with a little fix and a main() to test):

    #include 
    
    typedef int Var;
    struct Lit {
        int     x;
        // Use this as a constructor:
        friend Lit mkLit(Var var, bool sign = false);
        bool operator == (Lit p) const { return x == p.x; }
        bool operator != (Lit p) const { return x != p.x; }
        bool operator <  (Lit p) const { return x < p.x;  }
    };
    inline  Lit  mkLit(Var var, bool sign) { Lit p; p.x = var + var + (int)sign; return p; }
    
    int main()
    {
      Lit lit2 = mkLit(123, false);
      std::cout << "lit2.x: " << lit2.x << '\n';
      return 0;
    }
    

    Output:

    g++ (GCC) 8.1.0
    Copyright (C) 2018 Free Software Foundation, Inc.
    This is free software; see the source for copying conditions.  There is NO
    warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
    
    lit2.x: 246
    

    Live Demo on coliru

    Ah, yepp. Runs fine.

    Then I did the same on wandbox this time with clang HEAD 8.0.0:

    Start
    
    prog.cc:7:16: error: friend declaration specifying a default argument must be a definition
        friend Lit mkLit(Var var, bool sign = false);
                   ^
    prog.cc:12:14: error: friend declaration specifying a default argument must be the only declaration
    inline  Lit  mkLit(Var var, bool sign) { Lit p; p.x = var + var + (int)sign; return p; }
                 ^
    prog.cc:7:16: note: previous declaration is here
        friend Lit mkLit(Var var, bool sign = false);
                   ^
    2 errors generated.
    
    1
    
    Finish
    

    Live Demo on wandbox

    Here we are.

    So, I tried to understand what clang complains about.

    Finally, I found that clang finds Lit Lit::mkLit() and this does not match Lit mkLit() defined later.

    This can be fixed by a prototype for mkLit() inserted before struct Lit:

    typedef int Var;
    
    Lit mkLit(Var var, bool sign = false);
    
    struct Lit {
        int     x;
        // Use this as a constructor:
        friend Lit mkLit(Var var, bool sign);
        bool operator == (Lit p) const { return x == p.x; }
        bool operator != (Lit p) const { return x != p.x; }
        bool operator <  (Lit p) const { return x < p.x;  }
    };
    

    Now, I get a new issue: Lit not (yet) known when proto defined for mkLit(). So, I need a forward declaration for Lit and end up with:

    #include 
    
    typedef int Var;
    
    struct Lit;
    Lit mkLit(Var var, bool sign = false);
    
    struct Lit {
        int     x;
        // Use this as a constructor:
        friend Lit mkLit(Var var, bool sign);
        bool operator == (Lit p) const { return x == p.x; }
        bool operator != (Lit p) const { return x != p.x; }
        bool operator <  (Lit p) const { return x < p.x;  }
    };
    inline  Lit  mkLit(Var var, bool sign) { Lit p; p.x = var + var + (int)sign; return p; }
    
    int main()
    {
      Lit lit2 = mkLit(123, false);
      std::cout << "lit2.x: " << lit2.x << '\n';
      return 0;
    }
    

    Output:

    Start
    
    lit2.x: 246
    
    0
    
    Finish
    

    Live Demo on wandbox

    Problem fixed.

    I must admit that I'm not sure whether g++ accepts the original version although it shouldn't or whether clang denies the original version although it shouldn't. My stomache feeling tends to the former (i.e. clang is correct)...


    After thinking again what happens in g++, I came to the conclusion that it does the following:

    1. accept the friend Lit Lit::mkLit() (which is never used)

    2. defining another Lit mkLit().

    To find out I "converted" struct Lit to class Lit and this brings an error in g++ as well:

    #include 
    
    typedef int Var;
    class Lit {
        int     x;
        // Use this as a constructor:
        friend Lit mkLit(Var var, bool sign = false);
        bool operator == (Lit p) const { return x == p.x; }
        bool operator != (Lit p) const { return x != p.x; }
        bool operator <  (Lit p) const { return x < p.x;  }
    };
    inline  Lit  mkLit(Var var, bool sign) { Lit p; p.x = var + var + (int)sign; return p; }
    
    int main()
    {
      Lit lit2 = mkLit(123, false);
      std::cout << "lit2.x: " << lit2.x << '\n';
      return 0;
    }
    

    Output:

    g++ (GCC) 8.1.0
    Copyright (C) 2018 Free Software Foundation, Inc.
    This is free software; see the source for copying conditions.  There is NO
    warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
    
    main.cpp: In function 'int main()':
    main.cpp:17:35: error: 'int Lit::x' is private within this context
       std::cout << "lit2.x: " << lit2.x << '\n';
    

    Live Demo on coliru

    So, the original version of OP just worked in g++ because struct Lit with public int Lit::x simply doesn't need the friend Lit::mkLit().

    Now, I'm a bit puzzled. Which one is right g++ or clang? I don't know.

提交回复
热议问题