C ++编译器选择输出流运算符的错误重载<<

时间:2016-03-22 11:31:13

标签: c++ operator-overloading operators overloading

我有一个非常简单的内联辅助类叫IpAddress,它有2个运算符重载<<将对象序列化为一组自定义二进制流(运算符作为模板函数)或将其输出到std :: ostream(非模板运算符函数)。

#include <cstdint>
#include <string>
#include <array>
#include <iostream>
#include <sstream>

typedef uint8_t byte;

class IpAddress: public std::array<byte,4>
{
    // ...

    template<class S>
    inline friend S& operator<<(S& left, const IpAddress& right) {
        left.setBytes(right.data(), 4);
        return left;
    }

    inline friend std::ostream& operator<< (std::ostream& left, const IpAddress& right) {
        // do stuff eligible for an ostream
        return left;
    }

    inline operator std::string() {
        std::stringstream stream;
        stream << *this;
        return stream.str();
    }
}

如您所见,还有一个运算符将地址转换为字符串。不幸的是,&lt;&lt;内部调用是选择错误的运算符&lt;&lt; (模板一),导致编译错误:

error: C2039: 'setBytes' : is not a member of 'std::basic_stringstream<char,std::char_traits<char>,std::allocator<char>>'

为什么编译器(在这种情况下为MSVC)使用非工作体选择错误的重载?为什么不更专业&#34; std :: ostream运算符?我该如何改变这种行为?谢谢!

1 个答案:

答案 0 :(得分:0)

您没有发布完整的可编辑示例,因此我看不到呼叫网站。

然而,问题很可能是因为您流出的内容不是完全 ostream。它可能来自ostream。在这种情况下,template T将是更好的匹配。

编辑:

除了样式评论(他们是对的,你会很好听),这是一个修复:

#include <iostream>
#include <utility>
#include <cstdint>
#include <array>
#include <sstream>


template<class T> static constexpr bool IsAnOstream = std::is_base_of<std::decay_t<T>, std::ostream>::value;

using byte = std::uint8_t;

struct IpAddress: public std::array<byte,4>
{
    // ...

    template<class S, std::enable_if_t<not IsAnOstream<S>>* = nullptr>
    friend S& operator<<(S& left, const IpAddress& right) {
        left.setBytes(right.data(), 4);
        return left;
    }

    friend std::ostream& operator<< (std::ostream& left, const IpAddress& right) {
        // do stuff eligible for an ostream
        return left;
    }

    /* 
     * this is a bad idea - it can lead to all kinds of confusion
     *
    inline operator std::string() {
        std::stringstream stream;
        stream << *this;
        return stream.str();
    }
     */
};

// this is better - it's less surprising.
std::string to_string(const IpAddress& r)
{
    std::stringstream stream;
    stream << *this;
    return stream.str();
}


struct MyStream
{
    void setBytes(const uint8_t* p, size_t len) {}
};

int main()
{
    IpAddress a;
    MyStream s;

    std::cout << a;
    s << a;

    return 0;
}