【问题标题】:How to convert anything to string implicitly?如何将任何内容隐式转换为字符串?
【发布时间】:2013-09-02 14:38:26
【问题描述】:

我的目标是设计一个装饰 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;
}

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

我的主要目标是编写这样的代码

class Foo {
    int _memberWithUnderscoreInName;
}

String s = Foo();

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

有没有简单的解决方案?

【问题讨论】:

  • 这与您的问题无关,我可能错了,但以下划线开头的名称不被认为是不好的做法吗?
  • 我认为这是为了避免在 C++11 的 to_string() 中输入 to_
  • 只使用std::stringstream有什么问题?
  • underscore topic 上好像有一些讨论,但是单前导下划线至少不保留,只要后面不跟大写字母。
  • @AdriC.S.:这样的名称很好,只要它们不在全局命名空间中,并且只有一个下划线,后面没有大写字母。如果您有兴趣,请在here 讨论血腥细节。

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


【解决方案1】:

而不是在周围的命名空间中声明输出运算符,只将其声明为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 &lt;&lt; value 的候选对象,如果没有其他候选对象,则会给出编译错误而不是运行时死亡螺旋。

【讨论】:

    【解决方案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;
    }
    

    【讨论】:

    • 尽管您的解决方案有效并避免了无限循环,但默认情况下它不会引发编译器错误,并且与将 operator &lt;&lt; 声明为 friend 相比,它相当复杂。
    • @McLeary 您可以考虑示例中提到的 static_assert。
    • 我同意,但正如我所说,我更喜欢默认编译器错误而不是强制错误。此示例运行良好(我测试过),但我更喜欢更简单的示例。
    猜你喜欢
    • 2013-10-21
    • 1970-01-01
    • 1970-01-01
    • 2018-11-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多