模板运算符的不明确重载
我正在研究两个定义真实数据类型和复杂数据类型的包装类。每个类都定义了重载的构造函数,以及四个算术运算符+、-、*、/和五个赋值运算符=、+=等。为了避免重复代码,我想到了在左右移动时使用模板函数运算符的手侧参数具有不同的数据类型:
// real.h
class Real {
public:
explicit Real(const double& argument) {...}
explicit Real(int argument) {...}
...
friend const operator*(const Real&; const Real&);
template <class T> friend const Real operator*(const Real&, const T&);
template <class T> friend const Real operator*(const T&, cont Real&);
// Here, T is meant to be a template parameter for double and int
// Repeat for all other arithmetic and assignment operators
};
// complex.h
class Complex {
public:
explicit Complex(const Real& realPart) {...}
explicit Complex(const Real& realPart, const Real& imaginaryPart) {...}
// Overload for double and int data types
...
friend const operator*(const Complex&, const Complex&);
template <class T> friend const Complex operator*(const Complex&, const T&);
template <class T> friend const Complex operator*(const T&, cont Complex&);
// Here, T is is a template parameter for Real, double and int
...
};
这里的问题是代码如下:
//main.cpp
void main() {
Complex ac(2.0, 3.0);
Real br(2.0);
Complex cc = ac * br;
}
返回编译器(gcc)错误'ac中'operator*'的不明确重载* br',因为编译器无法区分:
template
[with T = Real]; friend const Complex 运算符*(const Complex&, const T&) template
[with T = Complex]; friend const Real operator*(const T&, cont Real&)
有没有办法在模板运算符*定义中指定 T 不能是 Complex真实类?或者我是否必须不用模板并为参数数据类型的每种可能的组合定义每个运算符?或者有没有办法重新设计代码?
I am working on two wrapper classes that define real and complex data types. Each class defines overloaded constructors, as well as the four arithmetic operators +,-,*,/ and five assignment operators =,+= etc. In order to avoid repeating code, I was thinking of using template functions when the left- and right-hand-side arguments of an operator are of a different data type:
// real.h
class Real {
public:
explicit Real(const double& argument) {...}
explicit Real(int argument) {...}
...
friend const operator*(const Real&; const Real&);
template <class T> friend const Real operator*(const Real&, const T&);
template <class T> friend const Real operator*(const T&, cont Real&);
// Here, T is meant to be a template parameter for double and int
// Repeat for all other arithmetic and assignment operators
};
// complex.h
class Complex {
public:
explicit Complex(const Real& realPart) {...}
explicit Complex(const Real& realPart, const Real& imaginaryPart) {...}
// Overload for double and int data types
...
friend const operator*(const Complex&, const Complex&);
template <class T> friend const Complex operator*(const Complex&, const T&);
template <class T> friend const Complex operator*(const T&, cont Complex&);
// Here, T is is a template parameter for Real, double and int
...
};
The problem here is that code like:
//main.cpp
void main() {
Complex ac(2.0, 3.0);
Real br(2.0);
Complex cc = ac * br;
}
returns the compiler (gcc) error ambiguous overload for 'operator*' in 'ac * br', as the compiler cannot tell the difference between:
template <class T> friend const Complex operator*(const Complex&, const T&)
[with T = Real]template <class T> friend const Real operator*(const T&, cont Real&)
[with T = Complex]
Is there a way to specify that T cannot be a Complex in the template operator* definition in the class Real? Or do I have to do without templates and define each operator for every possible combination of argument data types? Or is there a way to redesign the code?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
啊,运算符的问题...
Boost 创建了一个很好的库,以便通过提供最少的逻辑,所有其他变体都会自动为您添加!
看看Boost.Operators!
现在,对于您的问题,实际上正如您所注意到的,您必须定义两种类型的运算符(int 和 double),而不是使用通用模板。如果这些运算符中有很多逻辑(我对此表示怀疑),那么您始终可以让它们调用通用(模板化)方法。
但是,如果您使用 Boost.operators,则仅提供 Complex::operator*=(int) 和 Complex::operator*=(double) ,并且将自动推导出独立版本:)
(1) 您可以使用 pass by-如果所有参数都是内置参数,则此处的值。您可能还想考虑 Boost.CallTraits,它会自动在按值和按引用之间进行选择,具体取决于参数是否内置。对于模板来说很方便。
(2) 当按值返回参数时,将它们限定为 const 是没有意义的。 const 关键字仅意味着引用和指针,这里没有什么可以阻止用户实例化“简单”的
复杂
...幸运的是它没有!Ah, the problem of operators...
Boost created a nice library so that by providing a minimum of logic all the other variations are automagically added for you!
Take a look at Boost.Operators !
Now for your problem, actually as you noticed, you will have to define both flavors of the operators (int and double) rather than using a generic template. If there is a lot of logic in these operators (which I doubt), you can always have them call a common (templated) method.
But if you use Boost.operators you only provide Complex::operator*=(int) and Complex::operator*=(double) and the stand-alone versions will be automatically deduced :)
(1) You might use pass by-value here, if all arguments are built-ins. You might also want to consider Boost.CallTraits, which automatically chooses between by-value and by-ref depending if the argument is built-in or not. It is handy for templates.
(2) When returning arguments by value, it is non-sensical to qualify them as
const
. Theconst
keyword only means something for references and pointers, here nothing prevents the user to instantiate a 'simple'Complex
... and you are fortunate it doesn't!您可以使 Real 或 Complex 类具有非全局乘法运算符。
You could make either the Real or Complex class have non-global multiplication operators.
你能让复杂的构造函数显式化吗?
这意味着不允许从实数到复数的隐式转换,并且应该消除运算符 * 的歧义
Can you make Complex constructors explicit?
This will mean that the implicit conversion from Real to Complex is not allowed and should disambiguate the operator *
我有一些想法,但这个似乎可行。无论如何,我已经克服了你遇到的错误。
或者,您可能想查看 std::is_arithmetic 或其他方式来表示“实数或标量类型”或“不是复数”。
请查看
std::conjunction
上的此页面查看一个示例,其中如果类型位于类型列表中,则启用模板。I have a few ideas, but this one appears to work. I got past the error you were getting in any case.
Alternatively, you might want to look at
std::is_arithmetic
or other ways to say "a Real or scalar type" or "not a Complex".Check out this page on
std::conjunction
to see an example where they enable a template if a type is in a list of types.