C++ 为什么结构需要友元函数?

C++ 为什么结构需要友元函数?,c++,C++,我正在尝试建立一个我从互联网下载的源代码。当我试图编译它时,我得到了错误消息 friend declaration specifying a default argument must be the only declaration 以下是违规代码: typedef int Var; struct Lit { int x; // Use this as a constructor: friend Lit mkLit(Var var, bool sign = fa

我正在尝试建立一个我从互联网下载的源代码。当我试图编译它时,我得到了错误消息

friend declaration specifying a default argument must be the only declaration
以下是违规代码:

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; }
typedef int-Var;
结构照明{
int x;
//将其用作构造函数:
friend Lit mkLit(Var-Var,bool符号=false);
布尔运算符==(Lit p)常量{return x==p.x;}
布尔运算符!=(亮p)常量{return x!=p.x;}
布尔运算符<(Lit p)const{return x
我发现了几个问题可以解决这个问题,但问题相当深奥,我不太理解其中的解释。这表明我可以通过将
mkLit
的内联定义移到结构之前,从友元函数声明中删除默认参数,并将其移到内联de来解决问题定义,对吗

但更基本的是,我不明白为什么一个结构需要一个friend函数,因为它的成员无论如何都是公共的。对于依赖于参数的查找,已被接受的答案给出了一个答案(我的无知使我无法理解),但我看不出它是否适用于这种情况

删除友元函数声明并将默认参数移动到内联函数定义有什么缺点吗?如果有,你能给我一个简单的例子吗

但更基本的是,我不明白为什么一个结构需要一个朋友 函数,因为其成员无论如何都是公共的

<>这是一个误解。C++中没有结构和类,但是C++只有一个类,它们可以用关键字<代码>结构> <代码>或<代码>类<代码>来声明。唯一的区别是默认访问(即,除了它们的成员的顺序,如果您的地址有问题),它们是相同的:

相同:

class foo : bar {    
    int y;
public:    
    int x;
};
使用
class
struct
声明一个类纯粹是一个惯例问题。因此,您的问题转化为“为什么一个类需要一个友元函数?”答案是:允许友元访问私有字段

这是关于内联定义友元函数,而不是仅仅声明它,即

struct foo { 
    friend void foofriend() { /*put implementation here*/ }
};
vs


这确实与ADL有关(我也无法解释),并且与朋友的好处有点正交。

正如许多人之前提到的那样,
struct
相同,只是其成员默认为
公共
。它可以有私人成员

我在这里猜测,但我认为让它成为朋友工厂函数背后的想法是,它库的设计者保留初始化将来添加的任何私有成员的权利

回答您的问题:

  • 将默认参数移动到内联函数似乎有效
  • mkLit
    移动到
    struct
    之前是一个问题,因为它返回
    Lit
    ,并且
    Lit
    尚未定义(因为它现在显示在
    mkLit
    下面)
  • 删除
    struct
    中的
    friend
    声明将起作用,因为
    struct
    目前没有私有成员。如果您在原始库的较新版本中进行合并,则会添加私有成员(或通过将文本
    struct
    更改为
    class
    ,默认情况下使成员私有),然后它将停止工作

(注意:我将您的代码粘贴到中,并使用不同的编译器尝试了各种排列)

我花了一段时间才得到它,但最终我发现了

因此,我忽略了mis的主要标题(该标题已被IMHO充分使用),而专注于

指定默认参数的友元声明必须是唯一的声明

这引起了我的注意

因此,我不会重复关于
朋友
和访问公共成员的内容,因为我认为这个错误处理的是另一个问题

首先,我尝试了coliru上的OP示例(有一个小补丁和一个
main()
测试):

啊,是的,跑得很好

然后,这次我在wandbox上使用了
ClangHead8.0.0

开始
prog.cc:7:16:错误:指定默认参数的友元声明必须是定义
friend Lit mkLit(Var-Var,bool符号=false);
^
prog.cc:12:14:错误:指定默认参数的友元声明必须是唯一的声明
内联Lit mkLit(Var-Var,bool-sign){Lit p;p.x=Var+Var+(int)符号;返回p;}
^
进度cc:7:16:注:此处为先前声明
friend Lit mkLit(Var-Var,bool符号=false);
^
产生2个错误。
1.
完成

我们到了

所以,我试着去理解
clang
抱怨什么

最后,我发现
clang
查找
Lit::mkLit()
,这与后面定义的
Lit mkLit()
不匹配

这可以通过在
struct Lit
之前插入的
mkLit()的原型来解决:

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;  }
};
输出:

开始
lit2.x:246
0
完成

问题解决了

我必须承认,我不确定
g++
是否接受原始版本(虽然不应该),或者
clang
是否拒绝原始版本(虽然不应该)。我的胃痛倾向于前者(即
clang
是正确的)


在再次思考了在
g++
中发生的事情之后,我得出结论,它可以做到以下几点:

  • 接受好友Lit::mkLit()
  • (从未使用过)

  • 定义另一个
    Lit mkLit()

  • 为了找出答案,我将结构灯转换为类灯,这会带来一个错误
    struct foo {
        friend void foofriend();
    };
    
    void foofriend() { /*put implementation here*/ }
    
    #include <iostream>
    
    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;
    }
    
    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;  }
    };
    
    #include <iostream>
    
    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;
    }
    
    #include <iostream>
    
    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;
    }