来自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
如何成功定义运算符
(注意:运算符必须声明为朋友,因为更完全实现的实现依赖于私有变量。)
我发布了自己的答案,感谢约阿希姆·皮莱伯格和宋元耀的指导。
我简化了代码,只关注问题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
第一个问题:在行中
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 >运算符之间插入一个空格
为什么是<代码>
由于友元声明中的运算符==
引用函数模板,因此必须明确指定它。否则,将声明一个非模板函数,但稍后无法找到它的定义。它与调用(和实例化)函数模板不同。
注释 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 ++中,我会将所有属性和方法设为私有,然后将类声明为该类的朋友。 问题答案: 如果与之息息相关,为什么不使其成为一个内部类呢? 现在您不能从外面打电话: