我将函数的某些返回值绑定到const左值引用,但是在const lvalue引用的生命周期结束之前删除了该对象.
在以下示例中,Foo对象在foo结束的生命周期之前被销毁:
#include <iostream>
#include <string>
struct Foo
{
~Foo()
{
std::cout << "Foo destroyed: " << name << std::endl;
}
std::string name;
};
Foo&& pass_through(Foo&& foo)
{
return std::move(foo);
}
int main()
{
const Foo& foo = pass_through({"some string"});
std::cout << "before scope end" << std::endl;
}
Run Code Online (Sandbox Code Playgroud)
输出是:
Foo被破坏:
范围结束前的一些字符串
住在coliru:1
我以为你可以绑定const T&任何东西.返回是不好的做法T&&,应该按价值返回?
我在这里的cpprestsdk偶然发现了这个:
inline utility::string_t&& to_string_t(std::string &&s) { return std::move(s); }
Run Code Online (Sandbox Code Playgroud)
https://github.com/Microsoft/cpprestsdk/blob/master/Release/include/cpprest/asyncrt_utils.h#L109
非常混乱,因为Windows版本to_string_t(由预处理器宏调度)返回值:
_ASYNCRTIMP utility::string_t __cdecl …Run Code Online (Sandbox Code Playgroud) 在阅读了大量有关右值参考的文章后,我知道:
std::string&& f_wrong()
{
std::string s("hello");
return std::move(s);
}
Run Code Online (Sandbox Code Playgroud)
是错的,并且:
std::string f_right()
{
std::string s("hello");
return s;
}
Run Code Online (Sandbox Code Playgroud)
足以使std::string(或任何移动可构造类)的移动构造函数被调用.此外,如果要使用返回值来构造对象,则应用命名返回值优化(NRVO),该对象将直接在目标地址处构造,因此不会调用移动构造函数:
std::string s = f_right();
Run Code Online (Sandbox Code Playgroud)
我的问题是:何时是通过右值引用返回的好时机?就我所能想到的而言,似乎返回一个右值引用除了像std::move()和之类的函数之外没有意义std::forward().这是真的吗?
谢谢!
c++ rvalue-reference move-semantics return-value-optimization c++11
我一直在研究右值参考(对我来说是一个新概念),我对以下类函数中收到的警告感到困惑......
string&& Sampler::Serial() const {
stringstream ss;
.
. [assemble a string value using data members]
.
return ss.str();
}
Run Code Online (Sandbox Code Playgroud)
这成功编译,但有以下警告......
..\Metrics\Sampler.cpp:71:16: warning: returning reference to temporary [-Wreturn-local-addr]
return ss.str();
^
Run Code Online (Sandbox Code Playgroud)
我完全知道我正在返回一个临时的,正如我使用右值引用作为我的返回类型这一事实所证明的那样.代码似乎在执行时运行正常,那么为什么这需要编译器警告呢?
类似问题的标准答案似乎是复制返回值而不是使用引用,但是当我可以使用右值引用移动它时,为什么要复制潜在的大量临时数据?这不是它被发明的原因吗?
如果你看一下get辅助函数std::tuple,你会注意到以下的重载:
template< std::size_t I, class... Types >
constexpr std::tuple_element_t<I, tuple<Types...> >&&
get( tuple<Types...>&& t );
Run Code Online (Sandbox Code Playgroud)
换句话说,当输入元组本身是右值引用时,它返回一个右值引用.为什么不按值返回,调用move函数体?我的论点如下:get的返回将被绑定到引用,或者绑定到一个值(它可能被绑定到我想的任何东西,但这不应该是一个常见的用例).如果它与一个值绑定,那么移动构造无论如何都会发生.所以你不会因为价值回归而失去一切.如果绑定到引用,则返回右值引用实际上可能不安全.举个例子:
struct Hello {
Hello() {
std::cerr << "Constructed at : " << this << std::endl;
}
~Hello() {
std::cerr << "Destructed at : " << this << std::endl;
}
double m_double;
};
struct foo {
Hello m_hello;
Hello && get() && { return std::move(m_hello); }
};
int main() {
const Hello & x = foo().get();
std::cerr << x.m_double; …Run Code Online (Sandbox Code Playgroud) 哦,我在rvalue-references理解中发现了一个问题.问题:
int&& foo()
{
int n = 5;
return std::move(n);
}
int bar()
{
int y = 10;
return y;
}
int main()
{
int&& p = foo();
bar();
std::cout << p;
}
Run Code Online (Sandbox Code Playgroud)
编译器不写错误或警告我们从函数foo返回本地地址.我将在功能栏中将值5替换为10.但结果是5.如果我改变std :: move to static_cast编译器给出错误,结果为10.为什么会这样?我使用gcc 4.8.1.
在以下代码中:
class Myclass
{
public:
Myclass() = default;
~Myclass() = default;
Myclass(Myclass&&) = default;
Myclass& operator=(Myclass&&) = default;
Myclass(const Myclass&) = delete;
Myclass& operator=(const Myclass&) = delete;
};
Myclass GetObj()
{
Myclass obj;
return obj;
}
Myclass WrapperOfGetObj()
{
Myclass&& Wrapobj = GetObj();
return Wrapobj;
}
int main()
{
return 0;
}
Run Code Online (Sandbox Code Playgroud)
当编译它给在功能上的错误WrapperOfGetObj上return Wrapobj.错误是
'Myclass :: Myclass(const Myclass&)':尝试引用已删除的函数
这意味着它正在尝试调用deleted复制构造函数.据我所知Wrapobj是左值,当返回它的行为应该是相同obj的GetObj方法,即,调用移动构造函数在时return.
那么为什么要寻找复制构造函数呢?我在这里错过了什么?
我的问题是我必须在复制常量数据和移动非常量数据之间做出选择,而且我从概念上看不出为什么在我的情况下将常量数据移动到常量目标是不安全的。我也知道从 const 对象移动是不合逻辑的 - 所以也许还有其他一些方法可以实现我正在尝试做的事情......
我有一个 Builder 类,它设置了一个我不想复制的 BigData 对象。构建器完成后,它会将数据“发出”为常量,因为在此之后不应对其进行修改。构建器是临时的,然后可以被丢弃,但数据应该继续存在,作为可以查询它的包装类中的 const 成员。builder 是一个泛型类型(不知道数据的细节),而 wrapper 是特定于数据内容的。这是通过数据中的类型擦除实现的,但这与此问题无关,只是要明确表示不希望合并构建器和包装器。
以下代码描述了上述内容,除非您删除 BigData 的移动构造函数,否则不会编译。编译错误表明这段代码会复制数据,这是不可取的:
prog.cpp:26:57: 错误:使用已删除的函数 'constexpr BigData::BigData(const BigData&)'
包装器(const BigData&& In):数据(std::move(In)){}
^
class BigData
{
// Only a builder can create data
friend class Builder;
BigData() = default;
public:
// Move only
BigData( BigData&& ) = default;
};
class Builder
{
BigData Data;
public:
const BigData&& Build() & { return std::move(Data); }
// Functions to set up data go here
};
class Wrapper
{
const …Run Code Online (Sandbox Code Playgroud)