当前位置: 首页 > 知识库问答 >
问题:

C类模板中的朋友比较和关系运算符

宰父保臣
2023-03-14

来自Lippman et al C Primer第5版,第16.1.2节:

//forward declarations needed for friend declarations in Blob
template <typename> class BlobPtr;
template <typename> class Blob;
template <typename T> bool operator==(const Blob<T>&, const Blob<T>&)

template <typename T> class Blob {
   friend class BlobPtr<T>;
   friend bool operator==<T>(const Blob<T>&, const Blob<T>&);
}

第一个问题:排队

friend bool operator==<T>(const Blob<T>&, const Blob<T>&);

为什么是<代码>

friend bool operator==(const Blob<T>&, const Blob<T>&);

我添加了以下代码来定义运算符==并实例化类模板。它成功编译和链接:

template <typename T>
bool operator==(const Blob<T> &lhs, const Blob<T> &rhs) {return true;}

int main() {
    Blob<int> a, b;
    a == b;
}

如果我删除

Undefined symbols for architecture x86_64: "operator==(Blob<int> const&, Blob<int> const&)", referenced from: _main in partial_blob-3ccda9.o

显然

第二个问题:如果我想定义关系小于运算符< code >

1) 向前声明运算符

2)将操作员声明为好友,插入附加

3)定义类外运算符。

因此,我添加了以下代码:

template <typename T> bool operator<(const Blob<T>&, const Blob<T>&);
template <typename T> class Blob {
   //other members as before
   friend bool operator<<T>(const Blob<T>&, const Blob<T>&);
}
bool operator<(const Blob<T>&, const Blob<T>&) {return true;}
int main() {
   //other statements as before
   a < b;
}

这会在运算符周围产生编译错误

friend bool operator<(const Blob<T>&, const Blob<T>&);

然后我得到一个链接器错误,类似于前面的链接器错误==

"operator<(Blob<int> const&, Blob<int> const&)", referenced from: _main in partial_blob-a85d5d.o

如何成功定义运算符

(注意:运算符必须声明为朋友,因为更完全实现的实现依赖于私有变量。)


共有3个答案

裴令秋
2023-03-14

我发布了自己的答案,感谢约阿希姆·皮莱伯格和宋元耀的指导。

我简化了代码,只关注问题1。Pileborg和Holt正确地指出重载

template <typename> class Blob;
template <typename T> bool operator==(const Blob<T>&, const Blob<T>&); //line 2

template <typename T> class Blob {
   friend bool operator==(const Blob<T>&, const Blob<T>&); //line 5
};

template <typename T>
bool operator==(const Blob<T> &lhs, const Blob<T> &rhs) {return true;} //line 9

int main() {
    Blob<int> a, b; //line 12
    a == b; //line 13
}

此代码在链接时产生错误。为了理解原因,我们将从标准中查看相关语言。

来自 C 14 标准 n4296, 14.5.4 (此处使用的术语摘要见底部)。

对于不是模板声明的朋友函数声明:

(1.1) — 如果友元的名称是限定的或非限定的 template-id,则友元声明是指函数模板的特化,否则,

(1.2)-如果朋友的名称是限定id,并且在指定的类或命名空间中找到匹配的非模板函数,则朋友声明引用该函数,否则,

(1.3)-如果友元的名称是限定id,并且在指定的类或命名空间中找到匹配的函数模板,则友元声明引用该函数模板的推断专用化(14.8.2.6),否则,

(1.4)-名称应为声明(或重新声明)非模板函数的非限定id。

现在我们看一下第 5 行的朋友声明,根据上面列出的四个步骤确定它指的是什么。

(1.1)==不是模板id;继续前进。。。

(1.2)==不是合格id;继续前进...

(1.3) ==不是限定id;离开...

(1.4) 因此,== 是一个声明(或重新声明)非模板函数的非限定 id。

根据本标准第7.3.3节,友元<code>在最内部的封闭名称空间中声明,在本例中为全局名称空间。

当我们实例化Blob时

friend bool operator==(const Blob<int>&, const Blob<int>&);

因此,我们在全局命名空间中声明了运算符==的(非模板)重载,参数类型为const Blob

当在第12行调用a==b时,编译器开始重载解析过程。它首先查找与参数类型匹配的任何非模板重载。它以Blob时声明的运算符==的形式找到完美匹配

解决方案是在友元声明中使用template-id(不是模板声明)作为名称:

friend bool operator== <>(const Blob<T>&, const Blob<T>&)

或者

friend bool operator== <T>(const Blob<T>&, const Blob<T>&)

两个运算符==

当<code>Blob

friend bool operator== <>(const Blob<int>&, const Blob<int>&)

或者

friend bool operator== <int>(const Blob<int>&, const Blob<int>&)

在任何一种情况下,友元的名称都是一个非限定的模板id,因此通过上面的(1.1),友元声明引用了函数模板的专门化。然后,编译器为请求的找到最佳模板匹配

术语

限定 id:带有附加范围运算符的标识符,例如 std::string::i

unqualified-id:没有附加作用域运算符的标识符,例如< code>string或< code>i

temple-id:以下摘录(来自C 14 Standard n4296,14.2)总结了temple-id的结构:

简单模板id:

 template-name < template-argument-list (opt)>

模板id:

 simple-template-id

 operator-function-id < template-argument-listopt >

 literal-operator-id < template-argument-listopt>

模板名称:

 identifier

所以有些模板id会包含< code>Foo

田镜
2023-03-14

第一个问题:在行中

friend bool operator==<T>(const Blob<T>&, const Blob<T>&);

为什么是<代码>

friend bool operator==(const Blob<T>&, const Blob<T>&);

如果删除

警告:朋友声明'bool运算符==(const Blob

您正在将一个非模板化的函数作为您的类的朋友,因此编译器/链接器将寻找一个非模板化的函数,在您的情况下:

bool operator==(const Blob<int>&, const Blob<int>&);

…不存在,因此链接器无法找到它。

如果不添加

第二个问题:如果我想定义关系小于运算符

这是C代码解析方式的一个简单问题,您需要在< code >运算符之间插入一个空格

孙嘉悦
2023-03-14

为什么是<代码>

由于友元声明中的运算符==引用函数模板,因此必须明确指定它。否则,将声明一个非模板函数,但稍后无法找到它的定义。它与调用(和实例化)函数模板不同。

注释 T 可以省略,但

// refers to a full specialization of operator==
friend bool operator== <>(const Blob<T>&, const Blob<T>&);

另一种候选方法是在类声明中定义运算符,该类声明将是内联的,可以声明为非模板函数。例如:

template <typename T> class Blob {
   ...
   friend bool operator==(const Blob&, const Blob&) { 
       return ...;
   }
}

这会在运算符周围产生编译错误

是的,正如你所说,它应该写成朋友布尔运算符

 类似资料:
  • 我的C++代码示例中有一个很大的问题。“朋友”和“模板”有问题。 错误消息: Matrix.h:26:79:警告: 友元声明'std::oStream&MatrixClass::Operator<<(std::oStream&,const MatrixClass::Matrix&)'声明一个非模板函数[-wnon-template-friend]友元声明'std::oStream&Operator

  • 为了了解在朋友关系中使用Neo4J的优势,我在MySQL数据库上创建了一个Persons表(“Persons”,20900个数据集): 和一张关系表(“友谊”,每个人有50到100个朋友): 因此,大约有120万人的关系。 现在我想查看id=1的人的朋友的朋友的朋友的朋友,因此我创建了一个如下查询: 用户ID 1的查询用了大约30秒 在Neo4J中,我为每个人创建了一个节点(20900个节点)和一

  • 我试图使乘法运算符成为名为TVector3的模板类的朋友。我读过,我可以在类声明中声明朋友函数之前,对其进行前向声明,但我这样做的尝试是徒劳的。我知道我可以简单地定义friend函数而不是声明它,但我希望它能与前向声明技术一起工作。 特别是,我试图为我的案例实施这个解决方案。我发现这篇文章也是David Rodriguez给出的解决方案(第三个版本),但我不知道我做错了什么。 我使用'g temp

  • 我无法让它工作: 无法编译,错误消息如下: 无效使用temping-id'运算符*

  • 我有一个模板,其中有一个friend函数的声明,在类之外,我有它的实现: 在其他文件中的某个地方将其命名为什么签名? 我尝试过: 但它说它无法解决这个问题。为什么?朋友成员应该这样看待,不是吗? 编辑: 这是在Troll.cpp在它的功能。 仍会喊出“未在此范围内声明”、“函数无法解析”、“符号无法解析”、“之前应为主表达式”、“之前应为主表达式”

  • 问题内容: 您知道如何仅在特殊类中才能使对象可变吗?在此示例中,我希望对象PrivateObject在类内部只能是可更改的(可递增的),而在其他任何地方都不能更改。有没有办法做到这一点? 在C ++中,我会将所有属性和方法设为私有,然后将类声明为该类的朋友。 问题答案: 如果与之息息相关,为什么不使其成为一个内部类呢? 现在您不能从外面打电话: