【问题标题】:Compiler errors while doing implicit conversions for overloaded operators arguments对重载的运算符参数进行隐式转换时出现编译器错误
【发布时间】:2012-08-16 13:40:22
【问题描述】:

从一本书中做一些练习我遇到了一个疑问。我已经将一些运算符重载定义为类的友元函数 (Stonewt)。问题来自于这样的原型:

friend ostream & operator<<(ostream &os, Stonewt &st);
friend Stonewt operator+(Stonewt &st1, Stonewt &st2);

如果我依靠构造函数进行隐式转换并让编译器完成工作,如下所示(test1 和 test2 是类对象):

cout << "Summing both weights: " << test1 + test2;

我收到如下错误消息:

无法将“std::basic_ostream”左值绑定到“std::basic_ostream&&”

初始化 'std::basic_ostream<_chart _traits>& std::operator&&, const _Tp&) 的参数 1 [with _CharT = char; _Traits = std::char_traits; _Tp = Stonewt]'

OTOH,如果我执行以下操作,我不会收到任何错误:

Stonewt test3 = test1 + test2;
cout << "Summing both weights: " << test3;

就像编译器得到一个 Stonewt 对象,它可以转换为 Stonewt&(这是函数所期望的)。但如果它得到其他东西,它就无法迈出步伐,直到到达 Stonewt&。 (同样的事情发生在其他运算符重载(如 operator*)的其他实例中,如果我放一个 double 并期望编译器通过构造函数将其转换为 Stonewt,然后转换为 Stonewt&,它不起作用。我必须放一个 Stonewt对象)。

这是正确的行为吗?有什么帮助吗?

我会放整个程序以备不时之需:

类定义:

// stonewt1.h -- revised definition for the Stonewt class (for project Exercise 11.5.cbp)

#ifndef STONEWT1_H_
#define STONEWT1_H_

using std::ostream;

class Stonewt
{
public:
    enum {Lbs_per_stn = 14}; // pounds per stone
    enum Mode {STN, ILBS, FLBS}; // stone, integer pounds, and floating-point pounds modes
private:
    int stone; // whole stones
    double pds_left; // fractional pounds
    double pounds; // entire weight in pounds
    Mode mode; // state member
public:
    Stonewt(double lbs = 0.0, Mode form = FLBS); // construct from pounds
    Stonewt(int stn, double lbs, Mode form = STN); // construct from stones and pounds
    ~Stonewt(); // do-nothing destructor
    void reset(double lbs = 0);
    void reset(int stn, double lbs = 0);
    void set_mode(Mode form);
    Mode mode_val() const;
    friend ostream & operator<<(ostream &os, Stonewt &st);
    friend Stonewt operator+(Stonewt &st1, Stonewt &st2);
    friend Stonewt operator-(Stonewt &st1, Stonewt &st2);
    friend Stonewt operator*(Stonewt &st1, Stonewt &st2);
    // conversion functions
    explicit operator int() const;
    explicit operator double() const;
};

#endif

方法+友元函数+转换函数实现:

// stonewt1.cpp -- Stonewt class: methods, friend functions, and conversion functions implementation (compile alongside main.cpp)

#include <iostream>
#include "stonewt1.h"
using std::cout;

// construct from pounds (both arguments defaulted, form defaulted to FLBS)
Stonewt::Stonewt(double lbs, Mode form)
{
    stone = int (lbs) / Lbs_per_stn; // integer division
    pds_left = int (lbs) % Lbs_per_stn + lbs - int(lbs);
    pounds = lbs;
    mode = form;
}

// construct from stones and pounds (form defaulted to STN)
Stonewt::Stonewt(int stn, double lbs, Mode form)
{
    stone = stn;
    pds_left = lbs;
    pounds = stn * Lbs_per_stn + lbs;
    mode = form;
}

Stonewt::~Stonewt() // do-nothing destructor
{
}

// reset object data members (don't change mode)
void Stonewt::reset(double lbs)
{
    stone = int(lbs) / Lbs_per_stn;
    pds_left = int(lbs) % Lbs_per_stn + lbs - int(lbs);
    pounds = lbs;
}

void Stonewt::reset(int stn, double lbs)
{
    stone = stn;
    pds_left = lbs;
    pounds = stn * Lbs_per_stn + lbs;
}

// change object mode
void Stonewt::set_mode(Mode form)
{
    mode = form;
}

// returns mode
Stonewt::Mode Stonewt::mode_val() const
{
    return mode;
}

// friend functions
ostream & operator<<(ostream &os, Stonewt &st)
{
    if (st.mode == Stonewt::STN)
        os << st.stone << " stones and " << int(st.pds_left + 0.5) << " pounds.\n";
    else if (st.mode == Stonewt::ILBS)
        os << int(st.pounds + 0.5) << " pounds.\n";
    else if (st.mode == Stonewt::FLBS)
        os << int(st.pounds) << " pounds.\n";
    else
        os << "Invalid mode.";
    return os;
}

Stonewt operator+(Stonewt &st1, Stonewt &st2)
{
    Stonewt result;
    result.stone = int(st1.pounds + st2.pounds) / Stonewt::Lbs_per_stn;
    result.pds_left = int(st1.pounds + st2.pounds) % Stonewt::Lbs_per_stn + (st1.pounds + st2.pounds) - int(st1.pounds + st2.pounds);
    result.pounds = st1.pounds + st2.pounds;
    return result;
}

Stonewt operator-(Stonewt &st1, Stonewt &st2)
{
    Stonewt result;
    result.stone = int(st1.pounds - st2.pounds) / Stonewt::Lbs_per_stn;
    result.pds_left = int(st1.pounds - st2.pounds) % Stonewt::Lbs_per_stn + (st1.pounds - st2.pounds) - int(st1.pounds - st2.pounds);
    result.pounds = st1.pounds - st2.pounds;
    return result;
}

Stonewt operator*(Stonewt &st1, Stonewt &st2)
{
    Stonewt result;
    result.stone = int(st1.pounds * st2.pounds) / Stonewt::Lbs_per_stn;
    result.pds_left = int(st1.pounds * st2.pounds) % Stonewt::Lbs_per_stn + (st1.pounds * st2.pounds) - int(st1.pounds * st2.pounds);
    result.pounds = st1.pounds * st2.pounds;
    return result;
}

// conversion functions
Stonewt::operator int() const
{
    return int(pounds + 0.5);
}

Stonewt::operator double()const
{
    return pounds;
}

客户(未完成):

// main.cpp -- Exercising the revised Stonewt class: state member, operator overloadings (operator<<()
// replacing the show methods, operator+(), operator-(), operator*()) as friend functions.

#include <iostream>
#include "stonewt1.h"
#include <string>

int main()
{
    using std::cout;
    using std::cin;
    using std::string;

    cout << "***********************************************************************"
            "*********";
    cout << "*\n*\n*";
    cout.width(35);
    cout << "Menu:\n";
    (cout << "*\n*").width(10);
    (cout << "a. Reset").width(20);
    (cout << "b. Select").width(20);
    (cout << "c. Change mode").width(20);
    (cout << "d. Show").width(0);
    (cout << "\n*").width(10);
    (cout << "e. Sum").width(20);
    (cout << "f. Subtract").width(20);
    (cout << "g. Multiply").width(20);
    cout << "h. Menu";
    cout << "\n*\n*\n";
    cout << "***********************************************************************"
            "*********\n\n";
    Stonewt test1;
    Stonewt test2;
    Stonewt &sel = test1;
    char ch {'z'};
    cin.get(ch);
    switch (ch)
    {
        case 'A' :
        case 'a' :  if (sel.mode_val() == 3 || sel.mode_val() == 2)
                    {
                        cout << "Enter the pounds: ";
                        double p {0.0};
                        cin >> p;
                        sel.reset(p);
                    }
                    else if (sel.mode_val() == 1)
                    {
                        cout << "Enter the stones: ";
                        int s {0};
                        cin >> s;
                        cout << "Enter the remaining pounds: ";
                        double p {0.0};
                        cin >> p;
                        sel.reset(s, p);
                    }
                    else
                        cout << "Wrong mode.";
                    break;
        case 'B' :
        case 'b' :  {
                        cout << "Select object (1 for test1, 2 for test2): ";
                        int temp;
                        cin >> temp;
                        if (temp == 1)
                            sel = test1;
                        else
                            sel = test2;
                        break;
                    }
        case 'C' :
        case 'c' :  {
                        cout << "Select the desired mode (STN, FLBS or ILBS): ";
                        string temp;
                        cin >> temp;
                        if (temp == "STN")
                            sel.set_mode(Stonewt::Mode::STN);
                        else if (temp == "ILBS")
                            sel.set_mode(Stonewt::Mode::ILBS);
                        else if (temp == "FLBS")
                            sel.set_mode(Stonewt::Mode::FLBS);
                        else
                            cout << "Wrong mode. " << sel.mode_val() << " retained.";
                        break;
                    }
        case 'D' :
        case 'd' :  cout << sel;
                    break;
        case 'E' :
        case 'e' :  cout << "Summing both weights: " << test1 + test2;
                    break;
        case 'F' :
        case 'f' :  cout << "Subtracting the second weight from the first one: " << test1 - test2;
                    break;
        case 'G' :
        case 'g' :  {
                        cout << "Choose a factor: ";
                        double temp;
                        cin >> temp;;;
                        sel = sel * temp;
                        break;
                    }
    }
    return 0;
}

【问题讨论】:

    标签: c++ compiler-errors operator-overloading implicit-conversion friend-function


    【解决方案1】:

    您不能将非常量引用绑定到临时引用,因此您需要 ostream 运算符来获取 const 引用:

    ostream & operator<<(ostream &os, const Stonewt &st);
    

    为什么?因为当你这样做时:

    cout << "Summing both weights: " << test1 + test2;
    

    test1 + test2 返回一个 Stonewt 临时值,并且您的操作员尝试通过非常量引用来获取它。这是因为test3 不是临时的:

    Stonewt test3 = test1 + test2;
    cout << "Summing both weights: " << test3;
    

    【讨论】:

    • 非常感谢。我明白原因。对此我很头疼,找不到解决方案。
    【解决方案2】:

    您的 operator&lt;&lt; 应该采用 const 引用:std::ostream&amp; operator&lt;&lt;( std::ostream&amp; dest, Stonewt const&amp; object );

    【讨论】:

    • 知道诊断所指的过载是什么吗?流右值是怎么回事?
    • @Potatoswatter 看起来某处有一个模板化的operator&lt;&lt;。 (我的意思是第二个论点的模板。)
    • 没错。但它没有出现在发布的来源中。再说一次,我什至没有尝试通过 IDEone 运行该源代码。
    猜你喜欢
    • 1970-01-01
    • 2016-03-22
    • 2012-01-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-09
    相关资源
    最近更新 更多