How to convert anything to string implicitly?

六月ゝ 毕业季﹏ 提交于 2019-12-30 07:12:09

问题


My goal is to design a String class that decorates std::string in order to provide some functionality my program needs. One functionality I want to add is the ability to convert anything to my String implicitly in order to save some typing.

In order to achieve the implicitly conversion I designed the following class:

std::ostream& operator<<(std::ostream& o, const String& s);

class String {
public:
    template<typename t_value>
    String::String(t_value value) {
       std::ostringstream oss;
       oss << value;
      _str = oss.str();
    }
private:
    std::string _str;
}

This works fine with any type that has the <<operator defined. The problem came up with any class that doesn't have the stream operator. A compiler error would be fine but what I got is an infinity recursion since C++ tries to use my global << operator to try to convert to my String type.

My primary objective is to code like this

class Foo {
    int _memberWithUnderscoreInName;
}

String s = Foo();

And get a compiler error instead of an infinite loop in the constructor.

Is there a simple solution for this?


回答1:


Instead of declaring the output operator in the surrounding namespace, only declare it as a friend of the String class:

class String {
public:
    // This must be implemented inline here
    friend std::ostream& operator<<(std::ostream& o, const String& s) {
        return o << _str; // for example
    }

    template<typename t_value>
    String(t_value value) {
       std::ostringstream oss;
       oss << value;
      _str = oss.str();
    }
private:
    std::string _str;
};

Now it can only be found by argument-dependent lookup, and so will only be considered if the second argument really is of type String, not just convertible to it. Therefore, it won't be considered as a candidate for os << value in the constructor, giving a compile error rather than a run-time death spiral if there is no other candidate.




回答2:


This or similar should fix it:

namespace HasFormattedOutput {

    namespace Detail
    {
        struct Failure{};
    }

    template<typename OutputStream, typename T>
    Detail::Failure operator << (OutputStream&, const T&);

    template<typename OutputStream, typename T>
    struct Result : std::integral_constant<
        bool,
        ! std::is_same<
            decltype((*(OutputStream*)0) << std::declval<T>()),
            Detail::Failure
        >::value
    > {};
} // namespace HasFormattedOutput

template <typename T, typename OutputStream = std::ostream>
struct has_formatted_output
:   HasFormattedOutput::Result<OutputStream, T>
{};

class X  {
    public:
    X() {}

    template <typename T>
    X(const T& t) {
        static_assert(
             has_formatted_output<T>::value, 
             "Not supported type.");
        std::ostringstream s;
        s << t;
        str = s.str();
    }

    private:
    std::string str;
};
std::ostream& operator << (std::ostream& stream, const X&) { return stream; }

struct Y  {
    Y() {}
};

int main() {
    Y y;
    X x(y);
    return 0;
}


来源:https://stackoverflow.com/questions/18575676/how-to-convert-anything-to-string-implicitly

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!