【问题标题】:Getting ostream's Insertion Operators获取 ostream 的插入运算符
【发布时间】:2019-11-02 15:03:35
【问题描述】:

我有一个要包装ostringstream 的类。我是这样处理的:

class Foo {
    ostringstream os;
public:
    template <typename T>
    decltype(ostream() << T(), Foo)& operator <<(const T& param) {
        os << param;
        return *this;
    }
}

我的意图是免费获得为ostream 定义的任何运算符。但我得到了编译错误:

错误 C2893:无法专门化函数模板 unknown-type &amp;Foo::operator &lt;&lt;(const T &amp;)

我使用decltype 是不是错了?

【问题讨论】:

  • 您是否尝试使用decltype(ostream() &lt;&lt; T(), Foo) 作为 SFINAE?
  • 有什么理由不使用Foo&amp; 作为返回类型?
  • @NathanOliver 我的意思是我没有定义替代...插入。
  • 我会考虑为此创建一个派生自std::streambuf 的类型,以便您的Foo 实际上可以是具有所有功能的std::ostream。 Boost.Iostreams 可以提供帮助。一篇有点老但是不错的文章:gabisoft.free.fr/articles/fltrsbf1.html
  • 这对没有默认 ctor 的 T 不起作用,您需要 std::declval。

标签: c++ templates operator-overloading wrapper ostream


【解决方案1】:

std::ostream 没有默认构造函数,Foo 不是可以在decltype 中使用的表达式。相反,您可以在第一个表达式中直接使用os。为了更容易返回Foo&amp;,我会使用尾随返回类型并使用*this

template <typename T>
auto operator<<(const T& param) -> decltype(os << param, *this);

【讨论】:

  • 这可能是我的架构有问题,但我似乎无法处理Foo() &lt;&lt; endl。我会期望得到支持? ideone.com/b6nYrY
  • @JonathanMee 好吧,std::endl 是一个函数模板——所以为了让Foo() &lt;&lt; endl 工作,编译器需要有一种方法来选择函数的正确实例化。标准std::ostream 具有用于操纵器的硬编码重载(即operator&lt;&lt;(std::ostream&amp; (*manip)(std::ostream&amp;)))。如果你想让它起作用,你可能也应该这样做。
  • 我想你是在告诉我,我还需要从这个列表中实现 https://en.cppreference.com/w/cpp/io/basic_ostream/operator_ltlt 运算符 10、11 和 12。我的问题是为什么?这些只是接受函数指针的方法,对吗?那不应该已经在我的模板中定义了吗?
  • @JonathanMee 不,它们尚未定义。 Foo() &lt;&lt; endl 的问题完全template&lt;class T&gt;void f(T); template&lt;class&gt;void g(); f(g); 的问题相同。这会出错,因为gstd::endl 一样,不是函数,而是代表函数 的函数模板。编译器需要更多信息才能使用正确的模板参数实例化g。您可以手动执行Foo() &lt;&lt; endl&lt;char, char_traits&lt;char&gt;&gt;,但这很丑陋。因此,std::ostream 使用函数指针定义了自己的提取运算符...
【解决方案2】:

这纯粹基于0x499602D2 的回答和您指向missing overloads 10-12 的链接。

我不确定要使用什么函数来测试重载 11,但使用 std::hexstd::endl 测试了 10 和 12。

#include <iomanip>
#include <iostream>
#include <sstream>

class Foo {
private:
    std::ostringstream os{};

public:
    using char_type = std::ostringstream::char_type;
    using traits_type = std::ostringstream::traits_type;

    // generic, with perfect forwarding instead of "const T&"
    template<typename T>
    auto operator<<(T&& param) -> decltype(os << std::forward<T>(param), *this) {
        os << std::forward<T>(param);
        return *this;
    }

    // overload 10
    Foo& operator<<(std::ios_base& (*func)(std::ios_base&)) {
        func(os);
        return *this;
    }

    // overload 11
    Foo& operator<<(std::basic_ios<char_type, traits_type>& (*func)(
        std::basic_ios<char_type, traits_type>&)) {
        func(os);
        return *this;
    }

    // overload 12
    Foo& operator<<(std::basic_ostream<char_type, traits_type>& (*func)(
        std::basic_ostream<char_type, traits_type>&)) {
        func(os);
        return *this;
    }

    auto str() { return os.str(); }
};

int main() {
    Foo a;

    a << "Hello Worl";      // generic
    a << std::hex << 13;    // 10 + generic
    a << std::endl;         // 12

    std::cout << a.str() << "\n";
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-16
    • 1970-01-01
    • 1970-01-01
    • 2017-04-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多