简体   繁体   English

您如何解决模板运算符之间的歧义?

[英]How do you resolve ambiguity between template operators?

I've looked far and wide and everyone seems to have a slightly different issue than me.我已经看得很远了,每个人似乎都有与我略有不同的问题。

For simplicity say I have a template struct Complex<X> and I want it to have overloads for real values and at least other Complex.为简单起见,假设我有一个模板结构Complex<X> ,并且我希望它具有实际值的重载,至少还有其他 Complex。 As a rule, operations between double or Complex<double> and Complex<float> (on either side) should return Complex<double> .通常, doubleComplex<double>Complex<float> (在任一侧)之间的操作应返回Complex<double> I'm currently using deduction guides that work quite well for this, but other options are std::common_type_t<X,Y> , decltype(std::declval<X>()+std::declval<Y>()) , etc.我目前正在使用对此非常有效的演绎指南,但其他选项是std::common_type_t<X,Y>decltype(std::declval<X>()+std::declval<Y>()) , ETC。

(1) `auto operator+(X const&)`
(2) `friend auto operator+(X const&, Complex<X> const&)`
(2) `template<class Y> auto operator+(Y const&)`
(3) `template<class Y> auto operator+(Complex<Y> const&)`
(4) `template<class Y> friend auto operator+(Y const&, Complex<X> const&)`

Here's the problem.这就是问题所在。 If I write (1-2), then Complex<float> sees doubles as floats.如果我写 (1-2),那么Complex<float>将双打视为浮点数。 If I make that (2-3), then apparently adding Complex<double> is ambiguous between (2,3,4).如果我做到了 (2-3),那么在 (2,3,4) 之间添加Complex<double>显然是不明确的。 Non-template operators wouldn't be ambiguous, but please assume there are too many template arguments to name.非模板运算符不会有歧义,但请假设有太多模板参数需要命名。

Next I thought that the CV/references were to blame, but making (1-2) operators of X changed nothing.接下来,我认为应该归咎于 CV/references,但是使 (1-2) X的运算符没有任何改变。 This appears to be opposite the behavior of auto x which won't be a reference.这似乎与auto x的行为相反,它不会作为参考。

I tried adding assertions like static_assert(std::is_arithmetic_v<Y>) to (1-2) but they don't participate.我尝试将诸如static_assert(std::is_arithmetic_v<Y>)类的断言添加到 (1-2) 但它们不参与。

After trying static assertions that didn't help from the function body, I thought before I move on I should try enabling/disabling functions another way.在尝试了对函数体没有帮助的静态断言之后,我想在继续之前我应该​​尝试以另一种方式启用/禁用函数。 I remember this approach not working correctly in the past, so I didn't try it until much too late.我记得这种方法在过去不能正常工作,所以我没有尝试,直到为时已晚。

template<class Y>
auto operator+(Complex<X> const& x, Complex<Y> const& y)
-> std::enable_if<std::is_arithmetic_v<Y>,
    Complex<decltype(std::declval<X>(), std::declval<Y>())>> {/*...*/}

template<class Y>
auto operator+(Complex<X> const& x, Y const& y)
-> std::enable_if<std::is_arithmetic_v<Y>,
    Complex<decltype(std::declval<X>(), std::declval<Y>())>> {/*...*/}

template<class Y>
friend auto operator+(Y const& y, Complex<X> const& x)
-> std::enable_if<std::is_arithmetic_v<Y>,
    Complex<decltype(y, std::declval<X>())>> {/*...*/}

This isn't the code I'm using, I've paraphrased everything.这不是我正在使用的代码,我已经解释了所有内容。 Please let me know if I made a mistake somewhere.如果我在某个地方犯了错误,请告诉我。

This table shows the sum type of the row and column correctly.此表正确显示了行和列的总和类型。 If either field type has double precision then so does the result;如果任一字段类型具有双精度,则结果也是如此; the same goes if either type is complex.如果任何一种类型都很复杂,情况也是如此。

    add          f           d      Complex<f>  Complex<d>
     f           f           d      Complex<f>  Complex<d>
     d           d           d      Complex<d>  Complex<d>
Complex<f>  Complex<f>  Complex<d>  Complex<f>  Complex<d>
Complex<d>  Complex<d>  Complex<d>  Complex<d>  Complex<d>

As an aside, if you define assignment operators like operator+= , then Complex<X> only goes on the left (no friend functions) and only returns Complex<X>& (type is fixed.) It should probably accept the same types as operator+ or use operator+ directly, so it only rounds at assignment/conversion.顺便说一句,如果你定义像operator+=这样的赋值运算符,那么Complex<X>只会在左边(没有友元函数)并且只返回Complex<X>& (类型是固定的。)它可能应该接受与operator+或直接使用operator+ ,所以它只在赋值/转换时四舍五入。

template<class Y>
auto operator+=(Y const& y)
-> std::enable_if<std::is_arithmetic_v<Y>,
    Complex>& { /*...*/ }

I assume that you don't want to use std::complex for some reason.我假设您出于某种原因不想使用std::complex Now you have to decide the type of complex<float>+double ;现在你必须决定complex<float>+double的类型; is it going to loose some data?它会丢失一些数据吗?

  1. If the result is complex<float> , then precision bits of double are lost and large values are clamped to INF .如果结果是complex<float> ,则double的精度位会丢失,并且较大的值会被限制为INF
  2. If the result is double , then the imaginary part is gone.如果结果是double ,那么虚部就消失了。

Thus, the best of both worlds would be complex<double> .因此,两全其美将是complex<double> A closer looks reveals that your problem boils down to conversion/promotion to/from numeric types.仔细观察会发现您的问题归结为数字类型的转换/提升。 You are going to need conversion constructors:您将需要转换构造函数:

#include <concept>
template<std::floating_point numeric>
class complex{
public:
    constexpr complex(numeric const r=0,numeric const i=0);

    template<std::floating_point othern>
        requires std::constructible_from<numeric,othern>
        explicit(!std::convertible_to<othern, numeric>)
    constexpr complex(complex<othern> const&);
    //...

Now you can simply define:现在您可以简单地定义:

    //continue class declaration:
    friend auto const& operator(complex rgt, complex const& lft)
        {return rgt+=lft;};
    complex const& operator+=(complex const&);
    //...

This approach hits two birds with one stone.这种方法用一块石头击中两只鸟。 For completness I would define one conversion operator too:为了完整起见,我也会定义一个转换运算符:

    //still inside class complex:
    explicit constexper operator numeric() const
        {return this->real();};
    //...

The explicit specifiers - in declaration of conversion constructors and operator - conducts correct deduction and required implicit. explicit说明符 - 在转换构造函数和运算符的声明中 - 进行正确的推导和所需的隐式。 conversions转换

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM