C++ 返回不可压缩对象而不移动的变通方法

C++ 返回不可压缩对象而不移动的变通方法,c++,c++11,gcc,move-semantics,gcc4.9,C++,C++11,Gcc,Move Semantics,Gcc4.9,在我的API中,有一个函数返回std::istringstream 不可复制,但支持移动,因此在一致性编译器上返回本地std::istringstream不会有问题 但是,在GCC4.9上,有一个用于移动std::istringstream。 是否有一些变通方法可以让我使用std::istringstream,而不必从用户的角度更改API 建议的解决方法是使用unique\u ptr,这将改变API的语义。如果无法移动std::istringstream,那就没什么办法了 如果对象不可复制且不可

在我的API中,有一个函数返回
std::istringstream

不可复制,但支持移动,因此在一致性编译器上返回本地
std::istringstream
不会有问题

但是,在GCC4.9上,有一个用于移动
std::istringstream
。 是否有一些变通方法可以让我使用
std::istringstream
,而不必从用户的角度更改API


建议的解决方法是使用
unique\u ptr
,这将改变API的语义。

如果无法移动
std::istringstream
,那就没什么办法了

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

在meatime中,您可以返回一个
唯一的\u ptr
。如果您确实希望按值返回,那么可以返回一个可移动的包装器,其中包含
std::unique_ptr
,并提供与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 normally

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

在不使用move/copy构造函数的情况下返回类的方法是使用带有括号的init list的return语句:

class C {
    C() = default;
    C(const C&) = delete;
    C(C&&) = delete;
};

C make_C() { return {}; }

int main() {
    C&& c = make_C();
}

不幸的是,此初始化只考虑非显式构造函数,
std::istringstream
具有显式构造函数

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

struct myIStringStream : std::istringstream
{
    myIStringStream () = default;
};

myIStringStream make_istringstream()
{
    return {};
}

int main()
{
    std::istringstream&& iss = make_istringstream();
}

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

我们的目标是为gcc(<5)bug找到一个解决方案,其中
std::istringstream
没有提供一个move-ctor,在我想要返回不可复制且(有bug)不可移动的流的情况下,它可以工作。
如注释中所述,我实际上可以更改我的函数签名(至少在gcc<5上)以返回一个代理对象,该对象允许复制或移动,而无需更改新编译器/其他编译器上使用的代码的API

一位同事提出并实施的想法是围绕
std::istringstream
创建一个代理对象,该对象提供了一个类似的API,但也提供了一个复制ctor,它可以从复制自流手动创建并初始化一个新的内部
std::istringstream
。此代理仅在有问题的编译器上使用

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

#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
#如果!已定义(uuu GNUC_uuuu)| |(uu GNUC_uuu>=5)
使用字符串\u stream=std::istringstream;
#否则
//在GCC 5之前,istringstream没有移动构造函数。
//而使用stringstream_代理作为解决方法。
类stringstream\u代理
{
公众:
stringstream_proxy()=默认值;
//用一个值构造。
stringstream_代理(标准::字符串常量和值):
流(值)
{}
//复制构造函数。
stringstream_代理(const stringstream_代理和其他):
stream_u3;(other.stream_3;.str())
{
stream.setstate(其他.stream.rdstate());
}
void setstate(std::ios_base::iostate state){stream_u.setstate(state);}
//流式输出参数的值。
//如果无法进行转换,则流将进入失败状态,
//运算符bool将返回false。
模板
stringstream_代理和运营商>>(T&thing)
{
流东西;
归还*这个;
}
//获取字符串值。
std::string str()常量{return stream.str();}
std::stringbuf*rdbuf()常量{return stream..rdbuf();}
//检查流的状态。
//当最近的流操作失败时为False
运算符bool()常量{return!!stream_;}
~stringstream_proxy()=默认值;
私人:
std::istringstream;
};
使用string\u stream=stringstream\u代理;
#恩迪夫

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