无需移动ctor即可返回不可复制对象的解决方法

时间:2016-10-18 14:06:50

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

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

但是,在gcc 4.9上,移动std::istringstreamno support。 我是否可以使用std::istringstream而无需从用户的角度更改API?

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

3 个答案:

答案 0 :(得分:0)

如果你无法移动std::istringstream,那就没有太多办法了。

如果对象不可复制且不可移动,则无法按值返回。 如果您想支持新功能,最好为这些功能获得新的编译器。

在meatime中,你可以返回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受影响较小的方式。除此之外,我不知道任何解决方法。

答案 1 :(得分:0)

没有移动/复制构造函数返回类的方法是使用带有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

答案 2 :(得分:0)

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

目标是为gcc(&lt; 5)错误找到解决方法,其中std::istringstream没有提供移动ctor,以便在我想要返回联合国的情况下工作-copyable和(bugly-)不可移动的流 正如评论中所提到的,我实际上可以更改我的函数签名(至少在gcc&lt; 5上)以返回允许复制或移动的代理对象,而无需更改新/其他编译器上使用的代码的API。

同事建议并实施的想法是围绕std::istringstream创建一个提供类似API的代理对象,同时提供一个手动创建和初始化新内部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