我可以在c++中创建一个新的操作符吗?
Can I create a new operator in C++ and how?
MATLAB数组支持矩阵运算和元素运算。例如:M*N
、M.*N
。这是区分两种不同操作的一种非常直观的方法。如果我想在c++中实现类似的操作,我该怎么做?
我也可以创建一个新的操作符.*
吗?如果有,有人能给我一些指导吗?
没有,你不能过载op.*
:
[C++03 & C++11: 13.5/3]:
下列操作符不能重载:. .* :: ?:
在c++中,有一个预定义的操作符列表,其中大多数是可重载的。*不是)。此外,任何名称都可以用作操作符,如:
#include <iostream>
// generic LHSlt holder
template<typename LHS, typename OP>
struct LHSlt {
LHS lhs_;
};
// declare myop as an operator-like construct
enum { myop };
// parse 'lhs <myop' into LHSlt
template<typename LHS>
LHSlt<LHS, decltype(myop)> operator<(const LHS& lhs, decltype(myop))
{
return { lhs };
}
// declare (int <myop> int) -> int
int operator>(LHSlt<int, decltype(myop)> lhsof, int rhs)
{
int& lhs = lhsof.lhs_;
// here comes your actual implementation
return (lhs + rhs) * (lhs - rhs);
}
// strictly optional
#define MYOP <myop>
int main() {
std::cout << (5 <myop> 2) << ' ' << (5 MYOP 2);
}
免责声明:严格来说,这被翻译成(5 < myop) > 2
,也就是LHSlt<int, decltype(myop)>(5) > 2
。因此,在c++术语中,它不是一个新的"操作符",但它的使用方式完全相同,甚至在ADL中也是如此。另外,如果type很大,您可能希望存储const T&
。
请注意,您可以对任何可以在类外部定义的二进制操作符执行此操作;优先级是基于两边(<
和>
)的优先级。因此,您可以按此优先顺序拥有*myop*
, +myop+
, <<myop>>
, <myop>
, |myop|
。
如果你想要右结合性,就有点棘手了。您将需要一个RHS-holder和一个LHS-holder(后者在这里是LHSlt
),并使用周围的操作符,使右操作符的优先级高于左操作符,例如a |myop> b |myop>c
是a |myop> (b |myop> c)
。然后,您需要将类型和持有人类型的函数都作为lhs。
不能重载.*
(参见Lightness对标准文本的回答),但是,有趣的是,可以重载->*
(类似于可以重载->
但不能重载.
)。如果这足以区分,那就试试吧:
struct Int {
int i;
Int operator*(Int rhs) const { return Int{i * rhs.i}; }
Int operator->*(Int rhs) const { return Int{i + rhs.i}; }
friend std::ostream& operator<<(std::ostream& os, Int rhs) {
return os << "Int(" << rhs.i << ')';
}
};
int main() {
Int five{5};
Int six{6};
std::cout << (five * six) << ", " << (five ->* six) << 'n';
}
这将打印Int(30), Int(11)
。
不能,不幸的是您不能定义新的操作符—您只能重载现有的操作符(有一些重要的例外,例如operator.
)。即便如此,对于给定的操作符来说,对于具有非常清晰且没有争议的现有语义的类型,重载操作符通常也是一个好主意——例如,任何表现为数字的类型都是重载算术和比较操作符的好选择,但是您应该确保operator+
不会,比如说,减去两个数字。
MATLAB数组支持矩阵运算和元素运算。例如:M*N、M*N。这是区分两种不同操作的一种非常直观的方法。如果我想在c++中实现类似的操作,我该怎么做?
也可以创建一个新的操作符。*吗?如果有,有人能给我一些指导吗?
对于第一部分,您可以重载大多数操作符,也有一些操作符不能重载,c++中的操作符列表如下:
-
算法
-
+ (addition)
-
- (subtraction)
-
* (multiplication)
-
/ (division)
-
% (modulus)
-
-
位
-
^ (XOR)
-
| (OR)
-
& (AND)
-
~ (Complement)
-
<< (Shift Left, Insertion to Stream)
-
>> (Shift Right, Extraction from Stream)
作业 -
-
= (Assignment)
-
关系
-
== (Equality)
-
!= (Inequality)
-
> (Greater-Than)
-
< (Less-Than)
-
>= (Greater-Than Or Equal-To)
-
<= (Less-Than Or Equal-To)
-
逻辑
-
! (NOT)
-
&& (AND)
-
|| (OR)
-
复合作业
-
+= (Addition-Assignment)
-
-= (Subtraction-Assignment)
-
*= (Multiplication-Assignment)
-
/= (Division-Assignment)
-
%= (Modulus-Assignment)
-
&= (AND-Assignment)
-
|= (OR-Assignment)
-
^= (XOR-Assignment)
-
<<= (Shift-Left Assignment)
-
>>= (Shift-Right Assignment)
-
递增-递减-都有两种形式(前缀)和(后缀)
-
++ (Increment)
-
-- (Decrement)
下标-
-
[] (Subscript)
函数调用-
-
() (Function Call)
-
地址、引用、指针
-
operator&()
-
operator*()
-
operator->()
-
逗号
-
operator,()
-
成员引用
-
operator->()
-
operator->*()
内存管理-
-
new
-
delete
-
new[]
-
delete[]
-
- 转换
-
operator "type" () const
-
NON Modifiable operator -不能重载的operator
-
?: (Conditional - Ternary)
-
. (Member Selection)
-
.* (Member Selection With Pointer To Member)
-
:: (Scope Resolution)
-
sizeof() (Object Size Information)
-
typeid() (Object Type Information)
-
所以知道这个列表将有助于回答你的问题。你能创建一个"新操作符"吗?在c++中?不!如果你想在c++中实现类似的操作;我怎么能做到呢?
你有4个选择:要么重载一个已经存在的可以重载的操作符,编写一个函数或方法来完成你想要执行的计算类型,创建一个模板类型来完成你的工作,或者最后一个是最不常用的,但你也可以编写宏来完成它们。
有一个头只有数学API库,它经常与OpenGL图形API和OpenGL的着色器语言GLSL一起使用,这个库有许多与向量,矩阵,四元数等一起工作的功能,以及所有必要的函数和操作。这里是GLM的链接,你可以看看他们的文档以及他们的库实现,因为它是一个头文件库或API。这应该会让你对他们如何构造Vector和Matrix对象以及可以对它们进行的操作有一些了解。
顺便说一句:我正在寻求回答这个问题的部分。我也不寻求复制所有的信息在其他有价值的答案。赏金所寻求的东西与所问的问题不同,所以我不回答这个问题。
提供一个矩阵乘法实际上是相当简单的。由于我不打算描述数据结构来表示矩阵,也不打算在它们上面完全实现操作和有效性检查,所以我只提供一些框架来说明。
例1:operator*()
作为成员函数
class M // a basic matrix class
{
public:
// assume other constructors and members to set things up
M operator*(const M &rhs) const;
};
M M::operator*(const M &rhs) const
{
// implement checks on dimensions, throw an exception if invalid
M result;
// implement the multiplication (typical iterations) and store results in result
return result;
}
int main()
{
M a;
M b;
// set up elements of a and b as needed
M c = a*b; // this relies on M having appropriate constructor(s) to copy or move the result of a*b into c
M d;
d = a * b; // this relies on M having appropriate operator=() to assign d to the result of a*b
}
上面的代码将operator*()
作为成员函数实现。因此,从功能上讲,c = a*b
等同于c = a.operator*(b)
。const
限定符表示这样一个事实,即矩阵乘法a*b
通常不会改变a
或b
。
operator*()
作为非成员函数
现在,operator*()
也可以实现为非成员(可选的friend
),其骨架看起来像
class M // our basic matrix class, different operator *
{
public:
// assume other constructors and members to set things up
friend M operator*(const M &lhs, const M &rhs);
};
M operator*(const M &lhs, const M &rhs)
{
// implement checks on dimensions, throw an exception if invalid
M result;
// implement the multiplication (typical iterations) and store results in result
return result;
}
// same main() as before
注意,在这种情况下,a*b
现在相当于operator*(a, b)
。
如果你想同时使用这两种形式,需要注意避免歧义。如果提供了operator*()
的两种形式,它们在类似c = a*b
的语句中都是有效匹配,编译器无法选择一种形式而不是另一种形式。结果是代码无法编译。
operator*()
也可以重载operator*()
——例如,用一个标量乘以一个矩阵。
class M // a basic matrix class
{
public:
// assume other constructors and members to set things up
M operator*(const M &rhs) const; // as in first example
M operator*(double scalar) const; // member form
friend M operator*(double scalar, const M &rhs); // non-member form
};
M M::operator*(double scalar) const
{
M result;
// implement the multiplication (typical iterations) and store results in result
return result;
}
M operator*(double scalar, const M &m)
{
M result;
// implement the multiplication (typical iterations) and store results in result
return result;
}
int main()
{
M a;
M b;
// set up elements of a and b as needed
M c = b * 2.0; // uses the member form of operator*() above
M d;
d = 2.0*a; // uses the non-member form of operator*() above
}
上面的b*2.0
相当于对b.operator*(2.0)
的一次调用,2.0*a
相当于对非成员operator*(2.0, a)
的一次调用。成员形式一般只能用于左操作数为M
类型的表达式中。因此,如果只提供operator*()
的成员表单,2.0*a
将无法工作。
除了上面的歧义问题外,在重载操作符时还需要注意其他事项。
- 在语言规则中不可能改变操作符的优先级或结合性。因此,在表达式
a+b*c
中,*
的优先级总是高于+
。这也是为什么在c++中重载^
求幂不是一个好主意,因为^
的优先级比c++中的+
低(对整型进行按位操作)。因此,a + b^c
在c++中实际上相当于(a + b)^c
,而不是a + (b^c)
(任何具有基本代数知识的人都会期望)。 - 该语言指定了一组操作符,不能创建新的操作符。例如,c++中没有
**
,因此a ** b
将a
提升到b
的能力(其他语言可以做到),并且不可能创建一个。 - 不是所有的操作符都可以重载。
c++中不能重载的操作符之一是.*
。所以不可能像在Matlab中那样使用这样的运算符。我通常建议不要尝试使用其他操作符获得相同的效果,因为上述约束会影响到这一点(并导致表达式给出反直觉的行为)。相反,只需提供另一个命名函数来完成这项工作。例如,作为成员函数
class M
{
public:
// other stuff
M ElementWiseProduct(const M &) const;
};
大多数答案已经涵盖了哪些操作符可重载和不可重载,但没有一个回答讨论为什么有些操作符是可变的,而有些操作符不是。
以下是Bjarne Stroustrup(编写c++的家伙)的引用,我在这个stackoverflow答案中找到了。要特别注意第三段。
当我决定允许重载操作符->时,我自然会考虑是否为操作符。可以类似地重载。
当时,我认为下列实参是结论性的:如果obj是一个类对象,那么obj。M对该对象所属类的每个成员M都有意义。通过重新定义内置操作,我们尽量不让语言变得可变(尽管对于=和一元&,出于迫切需要,违反了该规则)。
如果允许。对于类X,我们将无法通过常规方法访问X的成员;我们必须使用指针和->但是->和,也可能被重新定义。我想要一个可扩展的语言,而不是一个可变的。
这些论点很重要,但不是决定性的。特别是在1990年,吉姆·阿德考克提议允许运营商超载。就像算子->是。
他的网站上的一个页面增加了一点:
可以定义自己的操作符吗?
对不起,没有。这种可能性已经考虑过好几次了,但每次我/我们都认为可能的问题大于可能的好处。
这不是语言技术问题。甚至当我在1983年第一次考虑它的时候,我就知道它可以如何实现。然而,我的经验是,当我们超越最琐碎的例子时,人们似乎对"显而易见的"事物有着微妙的不同看法。操作符用法的含义。一个经典的例子是A ** b ** c。假设**表示幂。现在a ** b ** c是指(a ** b) ** c还是a ** (b ** c)?我认为答案是显而易见的,我的朋友们也同意——然后我们发现,我们在哪个解决方案是显而易见的问题上意见不一。我的猜想是这样的问题会导致微妙的错误。
所以,虽然大多数操作符都可以重载,但在c++中从来没有打算让人们创建任意的操作符。
与定义一个名为(在本例中)operator*()
:
Matrix operator*(const Matrix &m1, const Matrix &m2) ...
其中Matrix
是您定义的用于表示矩阵的类。
正如其他答案所说,重载operator.*
是不可能的。
但我有一个很好的解决方案,看看这里。
您可以在operator-ish形式中提供任何方法,例如:
M <matrix_mul> N
- 一个更容易的拷贝分配操作符实现
- 如何使操作符的左操作数<<一个函数
- Assert()函数抛出一个错误——操作符有问题
- 我可以在c++中创建一个新的操作符吗?
- 在使用另一个类的类中重载操作符==
- 在可强制转换为另一个模板类型的类模板中重载赋值操作符
- 如何创建一个std::map的常数值,它仍然可以被[]操作符访问
- 输出一个pair列表(由重载操作符创建)
- 操作符重载- cout和cin——ostream函数,不能被引用——它是一个已删除的函数
- 为什么不是只有一个?复制构造函数和赋值操作符
- 我正在努力完成我的作业.在Distance类中创建一个重载*操作符
- 重载操作符来处理我自己的类作为std类是一个好做法吗?
- c++的另一个类成员和重载操作符
- 如何定义一个全局可见操作符
- 为什么编译器可以找到其中一个操作符重载,而不能找到另一个
- 定义操作符<为另一个结构体的两个对象在结构体中
- 如何在c++中创建一个操作符,将一个数组复制到另一个数组
- 从另一个操作符重载函数调用操作符重载函数:性能
- Size_t:一个操作符?(以及使用unordered_set的一种方法)
- 命名空间作用域中的操作符在全局作用域中隐藏另一个操作符