如何隐式地将任何东西转换为字符串?

时间:2013-09-02 14:38:27

标签: c++ string type-conversion implicit-conversion

我的目标是设计一个String类来装饰std :: string,以便提供我的程序所需的一些功能。我想要添加的一个功能是能够隐式地将任何东西转换为我的String,以节省一些输入。

为了实现隐式转换,我设计了以下类:

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;
}

这适用于任何定义了<<运算符的类型。任何没有流操作符的类都会出现问题。编译器错误没问题,但我得到的是无限递归,因为C ++试图使用我的全局<<运算符来尝试转换为我的String类型。

我的主要目标是像这样编码

class Foo {
    int _memberWithUnderscoreInName;
}

String s = Foo();

在构造函数中得到编译器错误而不是无限循环。

这有一个简单的解决方案吗?

2 个答案:

答案 0 :(得分:5)

不是在周围的命名空间中声明输出运算符,而是仅将其声明为String类的朋友:

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;
};

现在它只能通过参数依赖查找找到,因此只有在第二个参数确实是String类型时才会被考虑,而不仅仅是可转换为它。因此,它不会被视为构造函数中os << value的候选者,如果没有其他候选者,则会产生编译错误而不是运行时死亡螺旋。

答案 1 :(得分:2)

这个或类似的应该解决它:

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;
}