转换逻辑目标是什么类型
What type does the conversion logic target?
我不明白为什么在以下代码中表达式 C c3 = 5 + c;
尽管可以像上一个语句中的5个转换为C。
#include <iostream>
class C
{
int m_value;
public:
C(int value): m_value(value) {} ;
int get_value() { return m_value; } ;
C operator+(C rhs) { return C(rhs.m_value+m_value); }
};
int main()
{
C c = 10;
C c2 = c + 5; // Works fine. 5 is converted to type C and the operator + is called
C c3 = 5 + c; // Not working: compiler error. Question: Why is 5 not converted to type C??
std::cout << c.get_value() << std::endl; // output 10
std::cout << c2.get_value() << std::endl; // output 15
}
,因为如果超载操作员作为类的成员函数,则只有在该类的对象用作左操作数时才能调用。(而左操作数变为要调用成员函数的隐式*this
对象。)
二进制运算符通常作为非成员实现以维护对称性(例如,在添加复杂的数字和整数时,如果操作员 是复杂类型的成员函数,则只有
complex+integer
会编译,而不是integer+complex
)。
根据标准,[over.match.oper]/3
(强调我的)
对于一个单一操作员 @,带有CV UNQUALIFIED的类型操作数 版本是T1,适用于带有左操作数的二进制操作员 类型的CV UNCALIFIFIFIFIFIED版本是T1和类型的正确操作数 其CV UNCALIFIED版本是T2,四组候选功能, 指定的成员候选人,非成员候选人,内置 候选人和重写的候选人的构建如下:
- (3.1)如果T1是当前定义的完整类类型或类别的类 T1 ::操作员的查找([over.call.func]);否则,一组 会员候选人为空。
这意味着,如果左操作数的类型不是类类型,则成员候选人的集合为空;不考虑超载运算符(作为成员函数)。
您可以将其作为非成员功能重载,以允许左右操作的隐式转换。
C operator+(C lhs, C rhs) { return C(lhs.get_value() + rhs.get_value()); }
然后c + 5
或5 + c
都可以正常工作。
live
btw:这将导致一个临时对象被构造(从 int
到 C
),以调用非会员函数;如果您关心的话,可以添加所有三个可能的过载,如下所示。另请注意,这是一个权衡问题。
C operator+(C lhs, C rhs) { return C(lhs.get_value() + rhs.get_value()); }
C operator+(C lhs, int rhs) { return C(lhs.get_value() + rhs); }
C operator+(int lhs, C rhs) { return C(lhs + rhs.get_value()); }
,这里有一些有关何时使用普通,朋友或成员函数过载。
的建议在大多数情况下,该语言使您决定是否确定是否 您想使用普通/朋友或成员功能版本 超载。但是,两者中的一个通常比 其他。
与不修改左操作数的二进制操作员打交道 (例如操作员 ),通常是普通或朋友函数版本 首选,因为它适用于所有参数类型(即使 左操作数不是班级对象,也不是一类 可修改)。普通或朋友功能版本已添加 "对称"的好处,因为所有操作数变为明确的参数 (而不是左操作数变成 *和右操作数 成为明确的参数)。
与确实修改左操作数的二进制操作员打交道时 (例如操作员 =),通常首选成员函数版本。 在这些情况下,最左的操作数将始终是类型,并且 使对象被修改成为 *这是 * 自然的。因为最右操作数变成了明确的参数,所以 对于谁被修改和谁得到的人来说,没有任何困惑 评估。
您面临的理由将某些操作员过载定义为免费功能,即需要在需要隐式转换时。要查看引擎盖下发生的事情,请考虑操作员超载调用的详细形式:
C c2 = c.operator+(5); // Ok, c has this member function
C c3 = 5.operator+(c); // No way, this is an integer without members
您显然可以做的是
中的明确C
构造 C c3 = C{5} + c;
,但这不是针对C
之类的算术值类型。要使隐式结构成为可能,请定义超载为免费功能
auto operator + (C lhs, const C& rhs)
{
lhs += rhs;
return lhs;
}
现在,左侧操作数没有限制。请注意,操作员是根据+=
实施的(您必须实现它以进行上述代码编译),这是本主题中指出的好习惯:当您为自定义类型提供二进制operator +
时,类型还可以预期operator +=
可用。因此,为了减少代码重复,通常用+=
实现+
(对于所有其他算术操作数相同)。
进一步指出,这些操作数通常需要大量的样板代码。为了减少这一点,请考虑例如提升操作员库。为了生成所有标准算术运算符,基于最小的实际手写代码:
#include <boost/operators.hpp>
class C : private boost::arithmetic<C>
// ^^^^^^^^^^^^^^^^^^^^
// where the magic happens (Barton-Nackmann trick)
{
int m_value ;
public:
C(int value): m_value(value) {} ;
C& operator+=(const C& rhs) { m_value += rhs.m_value; return *this; }
C& operator-=(const C& rhs) { m_value -= rhs.m_value; return *this; }
C& operator*=(const C& rhs) { m_value *= rhs.m_value; return *this; }
C& operator/=(const C& rhs) { m_value /= rhs.m_value; return *this; }
const C& operator+() const { return *this; }
C operator-() const { return {-m_value}; }
int get_value() { return m_value; } ;
};
这是关于您的建议,您建议编译器可以隐式将左手参数转换为 C
的另一种说明("还原ad vassurdum"),从本质上讲,基本上会打开一个can蠕虫。简单地说,实际的语言规则说,在应用转换之前,要查找函数调用和呼叫(用户指定)运算符的名称查找 - 是为了找到候选人集。在这一点上,尚未考虑操作数类型,但是范围非常好。因此,只要用户指定运算符才在范围内,第一个参数的类型 do 是在范围内,当它的第一个参数是(CV-qualified)类型的类型。当候选人设置时,已经发现,编译器试图应用转换规则并对候选人进行排名。
(因此,您的问题有些误导,因为在您的示例中,我们甚至都不了解转换逻辑,而是已经空了名称。)
现在,想象一下,我们可以简单地更改语言,说第一个参数也可以在名称分辨率之前转换。这里需要一点手势,因为这意味着我们必须进行转换,查找名称,然后再进行转换,因此在实践中的工作方式肯定不清楚。无论如何,然后查看此示例:
struct B;
struct A
{
A(int);
A operator +(B) const;
};
struct B
{
B(int);
B operator +(B) const;
};
现在,1 + B{3}
应该做什么?显然,它可以转换为B{1} + B{3}
。但是谁说我们不能做A{1} + B{3}
呢?为什么B
的构造方法比A
的s首选?当然,我们可以说要么是B
是首选,因为,看看B{...}+B{...}
的良好和对称性(好的,我有点熟悉)。或者,我们可以采取更安全的途径,说该程序包含这种模棱两可的话,该程序是不明式的。但是还有更多的角案件要考虑,例如如果将B
的构造函数制成explicit
- 编译器(仍在或新的)错误应该出现,还是应该默默切换到可用的隐式转换为A
?
另一个不明确的观点是哪种范围(例如命名空间)的类型?如果您在例如中使用operator +
,那肯定会令人惊讶全局名称空间范围,编译器会挖掘某种类型的__gnucxx::__internal::__cogwheels::__do_something_impl
,请暗示操作数,然后对其进行操作。
还要注意,即使可以以合理和干净的方式指定该功能,也可能具有相当多的编译时间成本(实际上,在编译C 和原因之一时,超载分辨率已经是最大的成本之一。为什么编译C 代码比编译c)要长得多。
tl; dr:
- 有棘手的角案。
- 好处是边际(为什么不像其他人指出的那样使此类操作员自由职能)?
- 关于如何标准化这一点的讨论肯定会很长。
- 静态自动 constexpr t = { "red" , "black" , "green" } 是什么类型;派生到?
- 这是什么类型的C++语法,我应该采取什么步骤来理解这一点
- 这在C++是什么类型的错误?
- int(int)& 或 int(int) const &是什么类型?
- 我如何找出3D容器的元素是什么类型
- 在 c 和 c++ 中,二维数组的元素是什么类型?
- 这个 lambda 的参数是什么类型?
- 转换逻辑目标是什么类型
- 如果我创建一个修改值的迭代器,静态成员"reference"应该是什么类型?
- 这是什么类型的行为
- 给定以下内容,"function"是什么类型?
- C++:文本文件中的数据是什么类型的
- C++11 中的"auto var = {condition} ? 1 : 1.0"是什么类型?是双精度还是整数?
- C++中的 decltype(this) 是什么类型
- 如何知道模板<类型名称类型>是什么类型?
- 内置数组的大小是什么类型?
- c++ 11中的std::chrono::high_resolution_clock::now()是什么类型?
- 在Microsoft Visual C++中,光线跟踪器应该是什么类型的项目
- 如何为delete编写一个C包装器,既快速又释放给定的任何类型,而不告诉它是什么类型
- 如何编写一个函数,无论数组是什么类型都输出它