打印对象的文本表示

Printing textual representation of objects

本文关键字:表示 文本 对象 打印      更新时间:2023-10-16

我对C++还比较陌生。如果我的术语不正确,请原谅。我试着四处寻找我的问题的答案,但我找不到(可能是因为我不能正确地表达我的问题)。如果有人能帮我,我将不胜感激。

我正在尝试编写一个类,用于创建可能包含对象或本机类型的文本表示的字符串。本质上,我有

private:
    stringstream ss;

public:
    template< typename T >
    Message& operator<<( const T& value ) {
        ss << value;
        return *this;
    }

过载<lt;运算符获取一些值,并尝试将其流式传输到字符串流中。如果T类似于int,或者如果类T定义了方法operator std::string(),我认为我的编译器对此很满意。然而,如果T是类似于vector<int>的某种类型,那么它将不再工作,因为vector<int>没有定义operator std::string()

我是否可以重载这个运算符,以便如果T定义了operator std::string(),那么我打印文本表示,如果没有,我只打印它的地址?

谢谢。

这可以通过构建此处描述的has_insertion_operator类型特性来实现:https://stackoverflow.com/a/5771273/4323

namespace has_insertion_operator_impl {
typedef char no;
typedef char yes[2];
struct any_t {
  template<typename T> any_t( T const& );
};
no operator<<( std::ostream const&, any_t const& );
yes& test( std::ostream& );
no test( no );
template<typename T>
struct has_insertion_operator {
  static std::ostream &s;
  static T const &t;
  static bool const value = sizeof( test(s << t) ) == sizeof( yes );
};
}
template<typename T>
struct has_insertion_operator :
  has_insertion_operator_impl::has_insertion_operator<T> {
};

一旦我们有了这些,剩下的就相对简单了:

class Message
{
  std::ostringstream ss;
public:
  template< typename T >
  typename std::enable_if<has_insertion_operator<T>::value, Message&>::type
  operator<<( const T& value ) {
    ss << value;
    return *this;
  }
  template< typename T >
  typename std::enable_if<!has_insertion_operator<T>::value, Message&>::type
  operator<<( const T& value ) {
    ss << &value;
    return *this;
  }
};

也就是说,如果定义了插入运算符,则打印值,否则打印其地址。

这并不依赖于定义到-std::string运算符的转换——您只需要确保您的T实例使用operator <<"可打印"(通常在定义每个T的同一范围中实现,例如命名空间或全局范围)。

这里有一个例子-为转换运算符到std::string和流式运算符使用一些自定义特性:

#include <iostream>
#include <string>
template <class T>
struct traits
{
    template <typename Q>
    static auto hos(Q*) -> decltype(std::declval<const Q>().operator std::string());
    static char hos(...);
    constexpr static bool has_operator_string =
        sizeof hos((T*){0}) != 1;
    // ----
    template <typename Q>
    static auto isab(Q*) -> decltype(std::cout << std::declval<const Q>());
    static char isab(...);

    constexpr static bool is_streamable =
        sizeof isab((T*){0}) != 1;
};
struct S
{
    template <typename T>
    typename std::enable_if<
                 traits<T>::has_operator_string,
                 S&>::type
    operator<<(const T& value)
    {
        std::cout << "string() " << value.operator std::string() << 'n';
        return *this;
    }    
    template <typename T>
    typename std::enable_if<!traits<T>::has_operator_string && traits<T>::is_streamable, S&>::type
    operator<<(const T& value)
    {
        std::cout << "<< " << value << std::endl;
        return *this;
    }
    template <typename T>
    typename std::enable_if<
                 !traits<T>::has_operator_string &&
                 !traits<T>::is_streamable,
                 S&>::type
    operator<<(const T& value)
    {
        std::cout << "T& @" << &value << std::endl;
        return *this;
    }
};
struct X
{
    operator std::string() const { return "hi"; }
};
struct Y
{
};
int main()
{
    std::cout << "> main()" << std::endl;
    std::cout << "X() ";
    S() << X();
    Y y;
    std::cout << "Y y; ";
    S() << y;
    std::cout << "Y() ";
    S() << Y();
    std::cout << ""text" ";
    S() << "text";
    std::cout << "< main()" << std::endl;
}