C++ class 模板中的友元比较和关系运算符
Friend comparison and relational operators in C++ class template
来自 Lippman 等人的 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>&);
为什么 <T>
出现在 ==
之后?为什么不简单地写
friend bool operator==(const Blob<T>&, const Blob<T>&);
我添加了以下代码来定义 operator== 并实例化 class 模板。它成功编译和链接:
template <typename T>
bool operator==(const Blob<T> &lhs, const Blob<T> &rhs) {return true;}
int main() {
Blob<int> a, b;
a == b;
}
如果我在友元声明中删除 operator==
后面的 <T>
,我会收到链接器错误:
Undefined symbols for architecture x86_64: "operator==(Blob<int> const&, Blob<int> const&)", referenced from: _main in partial_blob-3ccda9.o
显然 operator==
后面的 <T>
是必要的,但为什么呢?
第二个问题:如果我想为相同的class定义关系小于运算符<
,我想我应该遵循适用于 ==
的模式:
1) 前向声明运算符
2) 将运算符声明为友元,插入额外的<T>
其功能我不明白
3) 定义运算符out-of-class.
因此我添加了以下代码:
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;
}
这会在 operator<<T>
附近产生编译错误,我认为是因为编译器将 <<
解释为插入运算符。但是如果我将朋友声明重写为
friend bool operator<(const Blob<T>&, const Blob<T>&);
然后我得到一个链接器错误,类似于 ==
:
之前的链接器错误
"operator<(Blob<int> const&, Blob<int> const&)", referenced from: _main in partial_blob-a85d5d.o
如何为这个 class 成功定义运算符 <
?
(注意:运算符必须声明为友元,因为更完全实现的实现依赖于私有变量。)
why is the <T>
present after ==
? Clearly the <T>
following operator==
is necessary, but why?
因为友元声明中的operator==
指的是函数模板,必须明确指定。否则会声明一个非模板函数,但后面找不到它的定义。这与调用(和实例化)函数模板的场景不同。
注意 T
可以省略,但仍然需要 <>
。如:
// refers to a full specialization of operator==
friend bool operator== <>(const Blob<T>&, const Blob<T>&);
另一种候选方法是定义运算符在class声明中,这将是内联的并且可以被声明为非模板函数。如:
template <typename T> class Blob {
...
friend bool operator==(const Blob&, const Blob&) {
return ...;
}
}
This produces a compilation error around operator<<T>
如你所说,应该写成friend bool operator< <T>(...)
,或者friend bool operator< <>(...)
,或者看我对非模板函数的建议朋友
First question: in the line
friend bool operator==<T>(const Blob<T>&, const Blob<T>&);
why is the <T>
present after ==
? Why not simply write
friend bool operator==(const Blob<T>&, const Blob<T>&);
如果删除 <T>
,gcc 给出警告:
warning: friend declaration 'bool operator==(const Blob< <template-parameter-1-1> >&, const Blob< <template-parameter-1-1> >&)
' declares a non-template function [-Wnon-template-friend]
您正在使非模板函数成为您 class 的朋友,因此 compiler/linker 将寻找非模板函数,在您的情况下:
bool operator==(const Blob<int>&, const Blob<int>&);
...不存在,因此链接器无法找到它。
如果你不添加<T>
(或<>
到friend
声明),你必须为每个类型定义一个函数,这可能不是你想要的。
Second question: If I want to define the relational less than operator < for the same class, I would guess that I should follow the pattern that worked for ==:
这是一个简单的C++代码解析方式的问题,需要在operator<
和<<
之间插入一个space。这与 C++11 之前存在的问题相同,您必须使用 vector<vector<int> >
而不是 vector<vector<int>>
因为 >>
.
我发布自己的答案,感谢 Joachim Pileborg 和 sonyuanyao 的指导。
我简化了代码以仅关注问题 1。 Pileborg 和 Holt 正确地指出重载 <
只需要一个 space 来帮助编译器解析。
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
}
此代码在 link 时产生错误。要了解原因,我们将从标准中查看相关语言。
来自 C++14 标准 n4296,14.5.4(请参阅底部了解此处使用的术语摘要)。
For a friend function declaration that is not a template declaration:
(1.1) — if the name of the friend is a qualified or unqualified template-id, the friend declaration refers to a specialization of a function template, otherwise,
(1.2) — if the name of the friend is a qualified-id and a matching non-template function is found in the specified class or namespace, the friend declaration refers to that function, otherwise,
(1.3) — if the name of the friend is a qualified-id and a matching function template is found in the specified class or namespace, the friend declaration refers to the deduced specialization of that function template (14.8.2.6), otherwise,
(1.4) — the name shall be an unqualified-id that declares (or redeclares) a non-template function.
现在我们看第 5 行的好友声明,根据上面列出的四个步骤确定它指的是什么。
(1.1) ==
不是模板 ID;继续...
(1.2) ==
不是合格的 id;继续...
(1.3) ==
不是合格的 id;继续...
(1.4) 因此,==
是声明(或重新声明)非模板函数的非限定 ID。
根据标准的第 7.3.3 节,朋友 ==
在最里面的封闭名称 space 中声明——在这种情况下,全局名称 space。
当我们在第 12 行实例化 Blob<int>
时,编译器通过用 int
替换 class Blob 中所有出现的 T
来生成源代码。编译器生成的代码中的友元声明如下:
friend bool operator==(const Blob<int>&, const Blob<int>&);
因此我们在全局名称 space 中声明了 operator==
的(非模板)重载,参数类型为 const Blob<int>&
.
当在第 12 行调用 a == b
时,编译器开始重载解析过程。它首先查找与参数类型匹配的任何非模板重载。它以实例化 Blob<int>
时声明的 operator==
的形式找到完美匹配。 linker 然后查找对应于最佳匹配声明的 operator==
的定义,但它找到了 none,因为 operator==(const Blob<int>&, const Blob<int>&)
从未真正定义过!
解决方案是使用模板 ID(不是模板声明)作为友元声明中的名称:
friend bool operator== <>(const Blob<T>&, const Blob<T>&)
或
friend bool operator== <T>(const Blob<T>&, const Blob<T>&)
operator== <>
和 operator== <T>
都是 template-id 的(见下面的术语);前者有一个从函数参数列表中推导出来的隐式模板参数列表,后者有一个显式模板参数列表。
当Blob<int>
在第12行实例化时,编译器为友元声明生成如下代码:
friend bool operator== <>(const Blob<int>&, const Blob<int>&)
或
friend bool operator== <int>(const Blob<int>&, const Blob<int>&)
在任何一种情况下,朋友的名字都是一个不合格的模板ID,所以根据上面的(1.1),朋友声明指的是一个函数模板的特化。然后编译器会找到与请求的 <int>
专业化匹配的最佳模板。它找到的唯一模板是在第 2 行中声明并在第 9 行中定义的模板,它会根据需要调用它。
术语
qualified-id:带有附加范围运算符的标识符,例如std::string
或 ::i
unqualified-id:没有附加作用域运算符的标识符,例如string
或 i
.
template-id:以下摘录(来自C++14 Standard n4296, 14.2)总结了template-id的结构:
simple-template-id:
template-name < template-argument-list (opt)>
template-id:
simple-template-id
operator-function-id < template-argument-listopt >
literal-operator-id < template-argument-listopt>
template-name:
identifier
所以一些模板 ID 会包含 Foo<T>
和 ==<T>
。 (== 是一个运算符函数 ID)。请注意,与模板声明不同,template <>
不包含在模板 ID 表达式中。
来自 Lippman 等人的 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>&);
为什么 <T>
出现在 ==
之后?为什么不简单地写
friend bool operator==(const Blob<T>&, const Blob<T>&);
我添加了以下代码来定义 operator== 并实例化 class 模板。它成功编译和链接:
template <typename T>
bool operator==(const Blob<T> &lhs, const Blob<T> &rhs) {return true;}
int main() {
Blob<int> a, b;
a == b;
}
如果我在友元声明中删除 operator==
后面的 <T>
,我会收到链接器错误:
Undefined symbols for architecture x86_64: "operator==(Blob<int> const&, Blob<int> const&)", referenced from: _main in partial_blob-3ccda9.o
显然 operator==
后面的 <T>
是必要的,但为什么呢?
第二个问题:如果我想为相同的class定义关系小于运算符<
,我想我应该遵循适用于 ==
的模式:
1) 前向声明运算符
2) 将运算符声明为友元,插入额外的<T>
其功能我不明白
3) 定义运算符out-of-class.
因此我添加了以下代码:
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;
}
这会在 operator<<T>
附近产生编译错误,我认为是因为编译器将 <<
解释为插入运算符。但是如果我将朋友声明重写为
friend bool operator<(const Blob<T>&, const Blob<T>&);
然后我得到一个链接器错误,类似于 ==
:
"operator<(Blob<int> const&, Blob<int> const&)", referenced from: _main in partial_blob-a85d5d.o
如何为这个 class 成功定义运算符 <
?
(注意:运算符必须声明为友元,因为更完全实现的实现依赖于私有变量。)
why is the
<T>
present after==
? Clearly the<T>
followingoperator==
is necessary, but why?
因为友元声明中的operator==
指的是函数模板,必须明确指定。否则会声明一个非模板函数,但后面找不到它的定义。这与调用(和实例化)函数模板的场景不同。
注意 T
可以省略,但仍然需要 <>
。如:
// refers to a full specialization of operator==
friend bool operator== <>(const Blob<T>&, const Blob<T>&);
另一种候选方法是定义运算符在class声明中,这将是内联的并且可以被声明为非模板函数。如:
template <typename T> class Blob {
...
friend bool operator==(const Blob&, const Blob&) {
return ...;
}
}
This produces a compilation error around
operator<<T>
如你所说,应该写成friend bool operator< <T>(...)
,或者friend bool operator< <>(...)
,或者看我对非模板函数的建议朋友
First question: in the line
friend bool operator==<T>(const Blob<T>&, const Blob<T>&);
why is the
<T>
present after==
? Why not simply writefriend bool operator==(const Blob<T>&, const Blob<T>&);
如果删除 <T>
,gcc 给出警告:
warning: friend declaration '
bool operator==(const Blob< <template-parameter-1-1> >&, const Blob< <template-parameter-1-1> >&)
' declares a non-template function [-Wnon-template-friend]
您正在使非模板函数成为您 class 的朋友,因此 compiler/linker 将寻找非模板函数,在您的情况下:
bool operator==(const Blob<int>&, const Blob<int>&);
...不存在,因此链接器无法找到它。
如果你不添加<T>
(或<>
到friend
声明),你必须为每个类型定义一个函数,这可能不是你想要的。
Second question: If I want to define the relational less than operator < for the same class, I would guess that I should follow the pattern that worked for ==:
这是一个简单的C++代码解析方式的问题,需要在operator<
和<<
之间插入一个space。这与 C++11 之前存在的问题相同,您必须使用 vector<vector<int> >
而不是 vector<vector<int>>
因为 >>
.
我发布自己的答案,感谢 Joachim Pileborg 和 sonyuanyao 的指导。
我简化了代码以仅关注问题 1。 Pileborg 和 Holt 正确地指出重载 <
只需要一个 space 来帮助编译器解析。
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
}
此代码在 link 时产生错误。要了解原因,我们将从标准中查看相关语言。
来自 C++14 标准 n4296,14.5.4(请参阅底部了解此处使用的术语摘要)。
For a friend function declaration that is not a template declaration:
(1.1) — if the name of the friend is a qualified or unqualified template-id, the friend declaration refers to a specialization of a function template, otherwise,
(1.2) — if the name of the friend is a qualified-id and a matching non-template function is found in the specified class or namespace, the friend declaration refers to that function, otherwise,
(1.3) — if the name of the friend is a qualified-id and a matching function template is found in the specified class or namespace, the friend declaration refers to the deduced specialization of that function template (14.8.2.6), otherwise,
(1.4) — the name shall be an unqualified-id that declares (or redeclares) a non-template function.
现在我们看第 5 行的好友声明,根据上面列出的四个步骤确定它指的是什么。
(1.1) ==
不是模板 ID;继续...
(1.2) ==
不是合格的 id;继续...
(1.3) ==
不是合格的 id;继续...
(1.4) 因此,==
是声明(或重新声明)非模板函数的非限定 ID。
根据标准的第 7.3.3 节,朋友 ==
在最里面的封闭名称 space 中声明——在这种情况下,全局名称 space。
当我们在第 12 行实例化 Blob<int>
时,编译器通过用 int
替换 class Blob 中所有出现的 T
来生成源代码。编译器生成的代码中的友元声明如下:
friend bool operator==(const Blob<int>&, const Blob<int>&);
因此我们在全局名称 space 中声明了 operator==
的(非模板)重载,参数类型为 const Blob<int>&
.
当在第 12 行调用 a == b
时,编译器开始重载解析过程。它首先查找与参数类型匹配的任何非模板重载。它以实例化 Blob<int>
时声明的 operator==
的形式找到完美匹配。 linker 然后查找对应于最佳匹配声明的 operator==
的定义,但它找到了 none,因为 operator==(const Blob<int>&, const Blob<int>&)
从未真正定义过!
解决方案是使用模板 ID(不是模板声明)作为友元声明中的名称:
friend bool operator== <>(const Blob<T>&, const Blob<T>&)
或
friend bool operator== <T>(const Blob<T>&, const Blob<T>&)
operator== <>
和 operator== <T>
都是 template-id 的(见下面的术语);前者有一个从函数参数列表中推导出来的隐式模板参数列表,后者有一个显式模板参数列表。
当Blob<int>
在第12行实例化时,编译器为友元声明生成如下代码:
friend bool operator== <>(const Blob<int>&, const Blob<int>&)
或
friend bool operator== <int>(const Blob<int>&, const Blob<int>&)
在任何一种情况下,朋友的名字都是一个不合格的模板ID,所以根据上面的(1.1),朋友声明指的是一个函数模板的特化。然后编译器会找到与请求的 <int>
专业化匹配的最佳模板。它找到的唯一模板是在第 2 行中声明并在第 9 行中定义的模板,它会根据需要调用它。
术语
qualified-id:带有附加范围运算符的标识符,例如std::string
或 ::i
unqualified-id:没有附加作用域运算符的标识符,例如string
或 i
.
template-id:以下摘录(来自C++14 Standard n4296, 14.2)总结了template-id的结构:
simple-template-id:
template-name < template-argument-list (opt)>
template-id:
simple-template-id operator-function-id < template-argument-listopt > literal-operator-id < template-argument-listopt>
template-name:
identifier
所以一些模板 ID 会包含 Foo<T>
和 ==<T>
。 (== 是一个运算符函数 ID)。请注意,与模板声明不同,template <>
不包含在模板 ID 表达式中。