c++ 临时/匿名对象的运算符查找是否不同?

euoag5mw  于 2022-11-27  发布在  其他
关注(0)|答案(1)|浏览(109)

我试图理解为什么命名和临时(匿名?)对象在查找一个基类中定义的操作符时表现得不一样。在下面的代码中,我在一个变量“mytype”周围做了一个 Package 器,我想通过编译器定义在double和std::complex之间切换。我想在这个 Package 器中添加一个使用基类的操作符(类似于boost::operators)。我感兴趣的是“main”中两行cout的区别(承认是强制的)。

#include <iostream>
#include <cmath>

#include <complex>

#ifdef COMPLEX
typedef std::complex<double> mytype;
#else
typedef double mytype;
#endif

template <typename DerivedType, typename integertype>
struct Base
{    
    friend DerivedType& operator +=(DerivedType& lhs, const integertype& rhs)
    {
        std::cout << "base += version" << std::endl;
        return lhs += mytype(rhs);
    }
};

struct Wrapper : public Base<Wrapper, unsigned>
{
    Wrapper(const mytype& rhs) : m_value(rhs) {}

    Wrapper(const unsigned& rhs) : Wrapper(mytype(rhs)) {}

    Wrapper& operator += (const Wrapper& rhs)
    {
        std::cout << "wrapper version" << std::endl;
        m_value += rhs.m_value;
        return *this;
    }

    Wrapper& operator += (const mytype& rhs)
    {
        std::cout << "wrapper mytype version" << std::endl;
        m_value += rhs;
        return *this;
    }
     
    mytype m_value;
};

int main()
{
    std::cout << (Wrapper(2.0) += 3u).m_value << std::endl;

    Wrapper t_MyWrapper(2.0);
    std::cout << (t_MyWrapper += 3u).m_value << std::endl;
}

如果不使用-DCOMPLEX进行编译,则会得到以下输出:

wrapper mytype version
5
base += version
wrapper mytype version
5

就我所知,main中的第一个输出忽略了Base中的操作符+=(Wrapper&,const unsigned&)。相反,它将unsigned提升为double(这比转换为Wrapper更优先),并调用操作符+=(Wrapper& const double&)。然而,'named object'确实调用了Base类型中的操作符。使用-DCOMPLEX编译会导致编译错误:

SimplifiedProblem.cxx: In function ‘int main()’:
SimplifiedProblem.cxx:47:32: error: ambiguous overload for ‘operator+=’ (operand types are ‘Wrapper’ and ‘unsigned int’)
   47 |     std::cout << (Wrapper(2.0) += 3u).m_value << std::endl;
      |                   ~~~~~~~~~~~~~^~~~~
SimplifiedProblem.cxx:28:14: note: candidate: ‘Wrapper& Wrapper::operator+=(const Wrapper&)’
   28 |     Wrapper& operator += (const Wrapper& rhs)
      |              ^~~~~~~~
SimplifiedProblem.cxx:35:14: note: candidate: ‘Wrapper& Wrapper::operator+=(const mytype&)’
   35 |     Wrapper& operator += (const mytype& rhs)
      |              ^~~~~~~~

考虑到将unsigned转换为std::complex并不比转换为“Wrapper”(两者都不是标量类型)“更好”,编译错误是有道理的,但我想了解为什么不使用基类型中的运算符。
将操作符+=(Wrapper&,const unsigned&)直接移到 Package 器中可以避免这个问题,而移除构造函数Wrapper(const unsigned&)可以解决-DCOMPLEX时的歧义。但是我想了解临时对象查找的规则是否不同,或者是否有其他原因导致了这种行为。

cx6n0qe3

cx6n0qe31#

3u常值会隐含转换成Wrapper,因此operator+=的两个多载都可能是编译器标记给您的程式码行中的候选者。
一个简单的解决方案是用explicit标记两个Wrapper构造函数,这样,它们将永远不允许隐式转换为Wrapper,但总是要求您像main的第一行那样键入转换。

相关问题