【问题标题】:Workaround for returning uncopyable object without a move ctor在没有移动 ctor 的情况下返回不可复制对象的解决方法
【发布时间】:2016-10-18 14:06:50
【问题描述】:

在我的 API 中,我有一个返回 std::istringstream 的函数。
std::istringstream class 是不可复制的,但支持移动,因此在符合要求的编译器上返回本地 std::istringstream 没有问题。

但是,在 gcc 4.9 上,no support 用于移动 std::istringstream。 是否有一些解决方法可以让我使用 std::istringstream 而无需从用户的角度更改 API?

建议的解决方法 here,使用 unique_ptr<std::istringstream> 将改变 API 的语义。

【问题讨论】:

  • 如果您无法更改返回类型,我看不出如何解决。
  • 好吧,假设我使用auto 获取结果(或者只是将其用作临时结果)-因此您可以返回其他内容。我现在认为可以创建一个具有相同接口的包装器并将其返回。
  • 包装器会遇到同样的问题。您无法将流移动到包装器中,也无法复制它,那么如何将包装器从函数中取出?
  • 因为无论如何这是一种解决方法,一位同事建议我可以复制包装器copy-ctor(或move-ctor)中的ios状态和流缓冲区。这将使代码在较旧的编译器上正常工作,但有一些潜在的性能成本。如果他不发布他的解决方案,我会的。
  • 那么不要使用 4.9。这就是所提出问题的答案。

标签: c++ c++11 gcc move-semantics gcc4.9


【解决方案1】:

如果你不能移动std::istringstream,那就没有办法了。

如果一个对象是不可复制和不可移动的,你不能按值返回它。 如果您想支持新功能,最好为这些功能获取新的编译器。

在此期间,您可以返回 unique_ptr。如果你真的很想按值返回,你可以返回一个包含std::unique_ptr<std::istringstream> 的可移动包装器,并提供与 istringstream 相同的接口。但是,这也会影响返回类型。


通过右值引用返回可能很诱人。您可以执行以下操作:

struct MyApiClass {

    std::istringstream&& get_stream() {
        return std::move(*_stream);
    }

private:
    std::unique_ptr<std::istringstream> _stream;
};

然后,用你的旧编译器,你可以像这样使用它:

std::istringstream&& stream = myApiClass.get_stream();

// use stream as long as myApiClass exists

使用新编译器的人可以这样使用它:

std::istringstream stream = myApiClass.get_stream();

// use stream normally

这是 api 受影响较小的方式。除此之外,我不知道任何解决方法。

【讨论】:

    【解决方案2】:

    没有移动/复制构造函数的返回类的方法是使用带有braced-init-list的return语句:

    class C {
        C() = default;
        C(const C&) = delete;
        C(C&&) = delete;
    };
    
    C make_C() { return {}; }
    
    int main() {
        C&& c = make_C();
    }
    

    Demo

    不幸的是,这个初始化只考虑了非显式构造函数,std::istringstream 有显式构造函数。

    一种解决方法是使用非显式构造函数创建子类:

    struct myIStringStream : std::istringstream
    {
        myIStringStream () = default;
    };
    
    myIStringStream make_istringstream()
    {
        return {};
    }
    
    int main()
    {
        std::istringstream&& iss = make_istringstream();
    }
    

    Demo

    【讨论】:

      【解决方案3】:

      回答我自己的问题以供完整性和将来参考。

      我们的目标是为 gcc (解决方法,其中 std::istringstream 不提供在我想要返回不可复制和(bugly-) 不可移动的流。
      正如 cmets 中所述,我实际上可以更改我的函数签名(至少在 gcc

      一个同事建议和实现的想法是围绕std::istringstream创建一个代理对象,它提供了类似的API,但也提供了一个copy-ctor,它从复制的文件中手动创建和初始化一个新的内部std::istringstream - 从流。此代理仅用于有问题的编译器。

      其自然栖息地的代码是here
      以下是相关部分:

      #if !defined(__GNUC__) || (__GNUC__ >= 5)
         using string_stream = std::istringstream;
      #else
          // Until GCC 5, istringstream did not have a move constructor.
          // stringstream_proxy is used instead, as a workaround.
         class stringstream_proxy
         {
         public:
            stringstream_proxy() = default;
      
            // Construct with a value.
            stringstream_proxy(std::string const& value) :
               stream_(value)
            {}
      
            // Copy constructor.
            stringstream_proxy(const stringstream_proxy& other) :
               stream_(other.stream_.str())
            {
               stream_.setstate(other.stream_.rdstate());
            }
      
            void setstate(std::ios_base::iostate state) { stream_.setstate(state); }
      
            // Stream out the value of the parameter.
            // If the conversion was not possible, the stream will enter the fail state,
            // and operator bool will return false.
            template<typename T>
            stringstream_proxy& operator >> (T& thing)
            {
               stream_ >> thing;
               return *this;
            }
      
      
            // Get the string value.
            std::string str() const { return stream_.str(); }
      
            std::stringbuf* rdbuf() const { return stream_.rdbuf(); }
      
            // Check the state of the stream. 
            // False when the most recent stream operation failed
            operator bool() const { return !!stream_; }
      
            ~stringstream_proxy() = default;
         private:
            std::istringstream stream_;
         };
         using string_stream = stringstream_proxy;
      #endif
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-05-30
        • 1970-01-01
        • 2022-07-14
        • 2021-01-22
        • 1970-01-01
        • 2021-05-29
        • 2016-03-13
        • 1970-01-01
        相关资源
        最近更新 更多