[英]Operator overloading for nested struct only working as member or friend function
This C++ code compiles and runs perfectly, as I expect:正如我所料,这段 C++ 代码可以完美地编译和运行:
template <typename T> struct S { T *p; };
template <typename T>
bool operator == (S<T> &a, S<T> &b) { return a.p == b.p; }
int main () { int i; S<int> a = {&i}, b = {&i}; return a == b; }
However, if I try to do the same with the inner struct of an outer struct...但是,如果我尝试对外部结构的内部结构执行相同的操作...
template <typename T> struct O { struct I {T *p;}; };
template <typename T>
bool operator == (O<T>::I &a, O<T>::I &b) { return a.p == b.p; }
int main () { int i; O<int>::I a = {&i}, b = {&i}; return a == b; }
... then it doesn't compile anymore (gcc version 8.3.0, Debian GNU/Linux 10): ...然后它不再编译(gcc 版本 8.3.0、Debian GNU/Linux 10):
1.cpp:4:25: error: declaration of ‘operator==’ as non-function
bool operator == (O<T>::I &a, O<T>::I &b) { return a.p == b.p; }
^
[...]
Why is it so?为什么会这样? I also do not understand the above error message.
我也不明白上面的错误信息。
Note that I'm aware that I can make it work by defining the operator as a member function of the inner struct:请注意,我知道我可以通过 将运算符定义为内部结构的成员 function来使其工作:
template <typename T>
struct O2 {
struct I2 {
T *p;
bool operator == (I2 &b) { return p == b.p; }
};
};
int main () { int i; O2<int>::I2 a = {&i}, b = {&i}; return a == b; }
However, if somehow possible, I'd rather use the non-member function version, because I find it more symmetric and therefore clearer.但是,如果可能的话,我宁愿使用非成员 function 版本,因为我发现它更对称,因此更清晰。
Also, partly by trial and error, I found that the following symmetric version works...此外,部分通过反复试验,我发现以下对称版本有效......
template <typename T>
struct O3 {
struct I3 { T *p; };
friend bool operator == (I3 &a, I3 &b) { return a.p == b.p; }
};
int main () { int i; O3<int>::I3 a = {&i}, b = {&i}; return a == b; }
... but I do not really understand what is happening above. ...但我真的不明白上面发生了什么。 First, given that a friend declaration "grants a function or another class access to private and protected members of the class where the friend declaration appears", I do not understand how it helps in the code above, given that we're always dealing with structs and therefore with public members.
首先,鉴于朋友声明“授予 function 或另一个 class 访问出现朋友声明的 class 的私有和受保护成员”,我不明白它在上面的代码中有何帮助,因为我们总是在处理结构,因此具有公共成员。
Second, if I remove the friend
specifier, then it doesn't compile anymore.其次,如果我删除
friend
元说明符,那么它就不会再编译了。 Also, the [...] operator== [...] must have exactly one argument
error message makes me think that in this case the compiler expects me to define a member function operator==
whose left operand is O3
, not I3
.此外,
[...] operator== [...] must have exactly one argument
错误消息让我认为在这种情况下,编译器希望我定义一个成员 function operator==
其左操作数是O3
,而不是I3
. Apparently, however, the friend
specifier changes this situation;然而,显然,
friend
说明符改变了这种情况; why is it so?为什么会这样?
First, the compiler gets confused by missing typename
.首先,编译器因缺少
typename
而感到困惑。 The error message really is confusing and can be silenced via:错误消息确实令人困惑,可以通过以下方式消除:
template <typename T>
bool operator == (typename O<T>::I &a, typename O<T>::I &b) {
return a.p == b.p;
}
Now the actual problem gets more apparent:现在实际问题变得更加明显:
int main () {
int i;
O<int>::I a = {&i}, b = {&i};
return a == b;
}
results in the error:导致错误:
<source>: In function 'int main()':
<source>:15:14: error: no match for 'operator==' (operand types are 'O<int>::I' and 'O<int>::I')
15 | return a == b;
| ~ ^~ ~
| | |
| | I<[...]>
| I<[...]>
<source>:8:6: note: candidate: 'template<class T> bool operator==(typename O<T>::I&, typename O<T>::I&)'
8 | bool operator == (typename O<T>::I &a, typename O<T>::I &b) {
| ^~~~~~~~
<source>:8:6: note: template argument deduction/substitution failed:
<source>:15:17: note: couldn't deduce template parameter 'T'
15 | return a == b;
| ^
It is not possible to deduce T
from a == b
, (@dfribs words)不可能从
a == b
推导出T
,(@dfribs 的话)
because
T
is in a non-deduced context in both of the function parameters of the operator function template;因为
T
在运算符 function 模板的两个 function 参数中都处于非推导上下文中;T
cannot be deduced fromO<T>::I&
, meaningT
cannot be deduced from any of the arguments to the call (and function template argument deduction subsequently fails).T
不能从O<T>::I&
推导出来,这意味着T
不能从调用的任何 arguments 推导出来(并且 function 模板参数推导随后失败)。
Sloppy speaking, because O<S>::I
could be the same as O<T>::I
, even if S != T
.草率地说,因为
O<S>::I
可能与O<T>::I
相同,即使S != T
也是如此。
When the operator is declared as member then there is only one candidate to compare a O<T>::I
with another (because the operator itself is not a template, ie no deduction needed).当运算符被声明为成员时,只有一个候选者可以将
O<T>::I
与另一个进行比较(因为运算符本身不是模板,即不需要推导)。
If you want to implement the operator as non member I would suggest to not define I
inside O
:如果您想将运算符实现为非成员,我建议不要在
O
中定义I
:
template <typename T>
struct I_impl {
T *p;
};
template <typename T>
bool operator == (I_impl<T> &a,I_impl<T> &b) {
return a.p == b.p;
}
template <typename T>
struct O {
using I = I_impl<T>;
};
int main () {
int i;
O<int>::I a = {&i}, b = {&i};
return a == b;
}
Your confusion about friend
is somewhat unrelated to operator overloading.您对
friend
的困惑与运算符重载有些无关。 Consider:考虑:
#include <iostream>
void bar();
struct foo {
friend void bar(){ std::cout << "1";}
void bar(){ std::cout << "2";}
};
int main () {
bar();
foo{}.bar();
}
Output: Output:
12
We have two definitions for a bar
in foo
.我们对
foo
中的bar
有两个定义。 friend void bar(){ std::cout << "1";}
declares the free function ::bar
(already declared in global scope) as a friend of foo
and defines it. friend void bar(){ std::cout << "1";}
将自由 function ::bar
(已在全局范围内声明)声明为foo
的友元并定义它。 void bar(){ std::cout << "2";}
declares (and defines) a member of foo
called bar
: foo::bar
. void bar(){ std::cout << "2";}
声明(并定义)一个名为bar
的foo
成员: foo::bar
。
Back to operator==
, consider that a == b
is a shorther way of writing either回到
operator==
,考虑a == b
是一种更短的写作方式
a.operator==(b); // member ==
or要么
operator==(a,b); // non member ==
Member methods get the this
pointer as implicit parameter passed, free functions not.成员方法获取
this
指针作为传递的隐式参数,自由函数则不然。 Thats why operator==
must take exactly one parameter as member and exactly two as free function and this is wrong:这就是为什么
operator==
必须只将一个参数作为成员,将两个参数作为自由 function,这是错误的:
struct wrong {
bool operator==( wrong a, wrong b);
};
while this is correct:虽然这是正确的:
struct correct {
bool operator==(wrong a);
};
struct correct_friend {
friend operator==(wrong a,wrong b);
};
Compiling C++ does not require compilers to solve the halting problem.编译C++不需要编译器解决停机问题。
template <typename T> struct O { struct I {T *p;}; };
template <typename T>
bool operator == (typename O<T>::I &a, typename O<T>::I &b) { return a.p == b.p; }
going from O<int>::I
to int
requires, in the general case, that the compiler solve the halting problem.在一般情况下,从
O<int>::I
到int
需要编译器解决暂停问题。 To demonstrate why:为了证明为什么:
template <typename T> struct O { struct I {T *p;}; };
template <> struct O<double> { using I=O<int>::I; };
template <> struct O<char> { using I=std::string; };
template <typename T>
bool operator == (typename O<T>::I &a, typename O<T>::I &b) { return a.p == b.p; }
now, O<int>::I
can be named O<double>::I
.现在,
O<int>::I
可以命名为O<double>::I
。 You can actually make the map from O<T>::I
to T
require inverting an arbitrary turing-complete function, as template specialization is turing-complete.您实际上可以使 map 从
O<T>::I
到T
需要反转任意图灵完备 function,因为模板特化是图灵完备的。
Rather than carving out a region of invertable dependent type maps, C++ simply says "do not invert dependent types" when doing template pattern matching of arguments. C++ 在进行 arguments 的模板模式匹配时,并没有划出一个可反转的依赖类型映射区域,而是简单地说“不要反转依赖类型”。
So所以
template <typename T>
bool operator == (typename O<T>::I &a, typename O<T>::I &b) { return a.p == b.p; }
will never deduce T
.永远不会推断出
T
。
Now you can make this work, but it requires that you define the inverse mapping without relying on template type deduction.现在您可以完成这项工作,但它要求您定义逆向映射而不依赖于模板类型推导。
By far the easiest is to make I
be defined outside O
.到目前为止,最简单的方法是让
I
在O
之外定义。 Failing that, you need to define a way to find O<T>
given O<T>::I
, like:如果做不到这一点,您需要定义一种方法来查找
O<T>
给定O<T>::I
,例如:
template <typename T> struct O {
struct I {using Outer=O<T>;T *p;};
};
template<class Inner>
using Outer=typename Inner::Outer;
template<class X>
struct Type0;
template<template<class...>class Z, class T0, class...Ts>
struct Type0<Z<T0,Ts...>>{ using type=T0; };
template<class X>
using Type0_t=typename Type0<X>::type;
we can then那么我们可以
template <class I> requires (std::is_same_v<I, typename O<Type0_t<Outer<Inner>>>::I>)
bool operator == (Inner const &a, Inner const &b) { return a.p == b.p; }
and your code works.并且您的代码有效。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.