4

我的目标是设计一个装饰 std::string 的 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();

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

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

4

2 回答 2

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

现在它只能通过依赖于参数的查找来找到,因此只有当第二个参数真的是 type 时才会考虑String,而不仅仅是可以转换为它。因此,它不会被视为os << value构造函数中的候选对象,如果没有其他候选对象,则会给出编译错误而不是运行时死亡螺旋。

于 2013-09-02T15:17:35.707 回答
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;
}
于 2013-09-02T14:47:50.617 回答