【问题标题】:Global overload of operator<< does not work, why?operator<< 的全局重载不起作用,为什么?
【发布时间】:2019-10-01 10:46:36
【问题描述】:

我了解到 运算符 可以通过使其成为类的友元函数来重载。 例如,

struct Test
{
    std::string d_data;
    Test(const std::string & data) : d_data{data} {}
    friend std::ostream & operator<<(std::ostream & ostr, const Test & obj)
    {
        ostr << obj.d_data << '\n';
        return ostr;
    }
};

int main()
{
    Test t1("one");
    std::cout << t1;
    Test t2("two");
    std::cout << t2;
}
one
two

这似乎按预期工作。

但是,我无法理解为什么同样的方法不适用于全局过载。

#include <iostream>
#include <ostream>
#include <string>

std::ostream & operator<<(std::ostream & os, const std::string & s)
{
    os << s << '\n';
    return os;
}

int main()
{
    std::cout << "stackoverflow";
    std::cout << "stackoverflow";
}

stackoverflowstackoverflow

应以换行符分隔字符串,但未按预期工作。

【问题讨论】:

  • 您声称有效的第一个示例根本不应该像您展示的那样有效。您显示的重载 operator&lt;&lt; 函数不会访问正在传递的对象 (tObj),因此符号 d_data 是未知的。它不应该建立。创建minimal reproducible example 时,请确保它确实复制了您指定的行为。
  • 你不能这样做。运算符中的类型都不是您的,因此该运算符不是您定义的。

标签: c++ operator-overloading


【解决方案1】:

您的运营商使用

std::cout << "stackoverflow";

需要从const char * 类型的对象(在字符串文字隐式转换为指向其第一个字符的指针之后)到std::string 类型的对象的用户定义转换。

但是标准的 basic_ostream 类已经有一个不需要这种转换的操作符

template<class charT, class traits>
basic_ostream<charT, traits>& operator<<(basic_ostream<charT, traits>&, const char*);

所以这个操作符被调用而不是你的操作符。

此外,在您的运营商内

std::ostream & operator<<(std::ostream & os, const std::string & s)
{
    os << s << '\n';
    return os;
}

自身存在递归调用。

您可以通过以下方式定义您的运营商

#include <iostream>
#include <string>

std::ostream & operator<<(std::ostream & os, const char *s)
{
    return std::operator <<( os, s ) << '\n';
}

int main()
{
    std::cout << "stackoverflow";
    std::cout << "stackoverflow";
}

得到预期的结果

stackoverflow
stackoverflow

【讨论】:

    【解决方案2】:

    注意"stackoverflow" 的类型是const char[],但不是std::string。这意味着不会调用您的重载,但会调用标准库中的重载(operator&lt;&lt;(std::basic_ostream) 会被调用,因为它是完全匹配的并且不需要从 const char[]std::string 的隐式转换。

    template< class Traits >
    basic_ostream<char,Traits>& operator<<( basic_ostream<char,Traits>& os,  
                                            const char* s );
    

    顺便说一句:因为ADL可以找到。

    【讨论】:

    • @uneven_mark 我意识到它与 OP 的情况不太匹配。 std命名空间的修改是UB,但OP实际上并没有修改它。而且,在自定义类型上提供重载应该没问题。
    【解决方案3】:

    您可以全局重载,但 "stackoverflow" 不是 std::string,因此不会使用您的。
    (而且标准库中已经有这样的重载了。)

    要查看它是否有效,请将您的第一个重载移出类定义并使其成为非朋友。
    它必须声明为friend 的唯一原因是您已经在类定义中声明了它,否则它将是一个成员函数。

    这将按您的预期工作:

    struct Test
    {
        std::string d_data;
        Test(const std::string & data) : d_data{data} {}
    };
    
    std::ostream & operator<<(std::ostream & ostr, const Test & obj)
    {
        ostr << obj.d_data << '\n';
        return ostr;
    }
    
    int main()
    {
        Test t1("one");
        std::cout << t1;
        Test t2("two");
        std::cout << t2;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-08-23
      • 2021-08-23
      • 2019-09-27
      • 1970-01-01
      • 1970-01-01
      • 2013-06-12
      • 1970-01-01
      相关资源
      最近更新 更多