仅作为成员或友元函数工作的嵌套结构的运算符重载 此C++代码编译和运行完全,正如我所期望的: 模板结构S{T*p;}; 模板 布尔运算符==(S&a,S&b){返回a.p==b.p;} intmain(){inti;sa={&i},b={&i};返回a==b;}

仅作为成员或友元函数工作的嵌套结构的运算符重载 此C++代码编译和运行完全,正如我所期望的: 模板结构S{T*p;}; 模板 布尔运算符==(S&a,S&b){返回a.p==b.p;} intmain(){inti;sa={&i},b={&i};返回a==b;},c++,templates,operator-overloading,friend,C++,Templates,Operator Overloading,Friend,但是,如果我尝试对外部结构的内部结构做同样的处理 模板结构O{struct I{T*p;};}; 模板 布尔运算符==(O::I&a,O::I&b){返回a.p==b.p;} intmain(){inti;O::iaa={&i},b={&i};返回a==b;} 。。。然后它不再编译(gcc版本8.3.0,Debian GNU/Linux 10): 1.cpp:4:25:错误:将“operator==”声明为非函数 布尔运算符==(O::I&a,O::I&b){返回a.p==b.p;} ^ [.

但是,如果我尝试对外部结构的内部结构做同样的处理

模板结构O{struct I{T*p;};};
模板
布尔运算符==(O::I&a,O::I&b){返回a.p==b.p;}
intmain(){inti;O::iaa={&i},b={&i};返回a==b;}
。。。然后它不再编译(gcc版本8.3.0,Debian GNU/Linux 10):

1.cpp:4:25:错误:将“operator==”声明为非函数
布尔运算符==(O::I&a,O::I&b){返回a.p==b.p;}
^
[...]
为什么会这样?我也不理解上面的错误消息

请注意,我知道我可以通过使用内部结构使其工作:

模板
结构氧{
结构I2{
T*p;
布尔运算符==(I2&b){返回p==b.p;}
};
};
intmain(){inti;O2::i2a={&i},b={&i};返回a==b;}
然而,如果可能的话,我宁愿使用非成员函数版本,因为我发现它更对称,因此更清晰

另外,部分通过反复试验,我发现以下对称版本可以工作

模板
结构O3{
结构I3{T*p;};
友元布尔运算符==(I3&a,I3&b){返回a.p==b.p;}
};
intmain(){inti;O3::i3a={&i},b={&i};返回a==b;}
。。。但我真的不明白上面发生了什么。首先,考虑到“授予函数或另一个类对出现友元声明的类的私有和受保护成员的访问权”,我不理解它在上面的代码中有什么帮助,因为我们总是处理结构,因此也处理公共成员


其次,如果我删除
friend
说明符,那么它就不会再编译了。另外,
[…]运算符==[…]必须正好有一个参数
错误消息使我认为在这种情况下,编译器希望我定义一个成员函数
运算符==
,其左操作数是
O3
,而不是
I3
。显然,
friend
说明符改变了这种情况为什么会这样?

首先,编译器会因为缺少
类型名而感到困惑。错误消息确实令人困惑,可以通过以下方式消除:

template <typename T>
bool operator == (typename O<T>::I &a, typename O<T>::I &b) { 
    return a.p == b.p; 
}

您对
friend
的混淆与运算符重载有些无关。考虑:

#include <iostream>

void bar();

struct foo {
    friend void bar(){ std::cout << "1";}
    void bar(){ std::cout << "2";}
};

int main () { 
    bar();
    foo{}.bar();
}

foo
中,我们对
bar
有两种定义
friend void bar(){std::cout首先,编译器因缺少
typename
而感到困惑。错误消息确实令人困惑,可以通过以下方式消除:

template <typename T>
bool operator == (typename O<T>::I &a, typename O<T>::I &b) { 
    return a.p == b.p; 
}

您对
friend
的混淆与运算符重载有些无关。请考虑:

#include <iostream>

void bar();

struct foo {
    friend void bar(){ std::cout << "1";}
    void bar(){ std::cout << "2";}
};

int main () { 
    bar();
    foo{}.bar();
}

<>代码> <代码> Foo。<代码>朋友Valb Bar():STO::CUT< P>编译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; }
现在,
O::I
可以命名为
O::I
。实际上,您可以将
O::I
映射到
T
需要反转任意图灵完备函数,因为模板专门化是图灵完备的

<>而不是雕刻一个可反转的依赖类型映射区域,C++在表示参数的模板模式匹配时简单地说“不反转依赖类型”。 所以

那么我们可以

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; }
模板要求(标准::是否相同)
布尔运算符==(内部常量&a,内部常量&b){返回a.p==b.p;}

和你的代码工作。

编译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; }
现在,
O::I
可以命名为
O::I
。实际上,您可以将
O::I
映射到
T
需要反转任意图灵完备函数,因为模板专门化是图灵完备的

<>而不是雕刻一个可反转的依赖类型映射区域,C++在表示参数的模板模式匹配时简单地说“不反转依赖类型”。 所以

那么我们可以

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; }
模板要求(标准::是否相同)
布尔运算符==(内部常量&a,内部常量&b){返回a.p==b.p;}

您的代码也可以工作。

相关,如果不是重复的话:相关,如果不是重复的话:…说起来不草率,因为
T
在运算符函数模板的两个函数参数中都处于非推导上下文中;
T
不能从
O::I&
中推导,这意味着
T
不能从任何ar中推导出来调用的guments(函数模板参数推断随后失败).@dfrib谢谢;)…说话不马虎,因为
T
在运算符函数模板的两个函数参数中都处于非推导上下文中;
T
不能从
O::I&
中推导,这意味着
T
不能从调用的任何参数中推导(函数模板参数推导随后失败)。@dfrib谢谢;)
struct wrong {
    bool operator==( wrong a, wrong b);
};
struct correct {
    bool operator==(wrong a);
};
struct correct_friend {
    friend operator==(wrong a,wrong b);
};
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; }
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; }
template <typename T>
bool operator == (typename O<T>::I &a, typename O<T>::I &b) { return a.p == b.p; }
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;
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; }