我有一种情况,我想用一个参数调用一个函数,并将结果返回到同一个参数中
foo = f(foo);
另外,我假设参数x很大,所以我不想调用它的复制构造函数,而是调用它的移动构造函数。最后,我不想通过引用传递参数,因为我想将函数 f 与另一个函数 g 组合在一起。因此,这样的事情
foo = g(f(foo));
有可能。现在,有了移动语义,这一切几乎都是可能的,如下面的程序所示
#include <iostream>
struct Foo {
Foo() {
std::cout << "constructor" << std::endl;
}
Foo(Foo && x) {
std::cout << "move" << std::endl;
}
Foo(Foo const & x) {
std::cout << "copy" << std::endl;
}
~Foo() {
std::cout << "destructor" << std::endl;
}
Foo & operator = (Foo && x) {
std::cout << "move assignment" << std::endl;
return *this;
}
Foo & operator = (Foo & x) {
std::cout << "copy assignment" << std::endl;
return *this;
}
};
Foo f(Foo && foo) {
std::cout << "Called f" << std::endl;
return std::move(foo);
}
Foo g(Foo && foo) {
std::cout << "Called g" << std::endl;
return std::move(foo);
}
int main() {
Foo foo;
foo = f(std::move(foo));
std::cout << "Finished with f(foo)" << std::endl;
foo = g(f(std::move(foo)));
std::cout << "Finished with g(f(foo))" << std::endl;
}
这个程序的输出是:
constructor
Called f
move
move assignment
destructor
Finished with f(foo)
Called f
move
Called g
move
move assignment
destructor
destructor
Finished with g(f(foo))
destructor
这是有道理的。现在,困扰我的是,当我们第一次调用 f 或组合时,移动构造函数后跟移动赋值运算符。理想情况下,我想使用复制 elison 来防止调用这些构造函数中的任何一个,但我不确定如何调用。具体来说,函数 f 和 g 调用 foo 上的 std::move 因为否则复制,而不是移动,构造函数被调用。这在 C++ 标准的第 12.8.31 和 12.8.32 节中有规定。具体来说,
When certain criteria are met, an implementation is allowed to omit the copy/move construction of a class object, even if the constructor selected for the copy/move operation and/or the destructor for the object have side effects. In such cases, the implementation treats the source and target of the omitted copy/move operation as simply two different ways of referring to the same object, and the destruction of that object occurs at the later of the times when the two objects would have been destroyed without the optimization. This elision of copy/move operations, called copy elision, is permitted in the following circumstances (which may be combined to eliminate multiple copies):
— in a return statement in a function with a class return type, when the expression is the name of a non-volatile automatic object (other than a function or catch-clause parameter) with the same cvunqualified type as the function return type, the copy/move operation can be omitted by constructing the automatic object directly into the function’s return value
因为我们返回一个函数参数,所以我们没有得到复制elison。此外:
When the criteria for elision of a copy operation are met or would be met save for the fact that the source object is a function parameter, and the object to be copied is designated by an lvalue, overload resolution to select the constructor for the copy is first performed as if the object were designated by an rvalue. If overload resolution fails, or if the type of the first parameter of the selected constructor is not an rvalue reference to the object’s type (possibly cv-qualified), overload resolution is performed again, considering the object as an lvalue. [ Note: This two-stage overload resolution must be performed regardless of whether copy elision will occur. It determines the constructor to be called if elision is not performed, and the selected constructor must be accessible even if the call is elided. —end note ]
因为我们返回一个函数参数,我们返回一个左值,所以我们被迫使用 std::move。现在,归根结底,我只想将内存移回参数中,同时调用移动构造函数和移动赋值运算符似乎太多了。感觉应该是单招或者copy elison。有办法实现吗?
在对@didierc 的回答的回复中,比评论允许的时间更长,从技术上讲,是的,这适用于这种情况。同时,更大的目标是允许具有多个返回的函数以不复制任何内容的方式组合在一起。我也可以使用移动语义来做到这一点,但它需要 C++14 的技巧才能工作。它也加剧了很多 Action 的问题。但是,从技术上讲,没有拷贝。具体来说:
#include <tuple>
#include <iostream>
#include <utility>
// This comes from the N3802 proposal for C++
template <typename F, typename Tuple, size_t... I>
decltype(auto) apply_impl(F&& f, Tuple&& t, std::index_sequence<I...>) {
return std::forward<F>(f)(std::get<I>(std::forward<Tuple>(t))...);
}
template <typename F, typename Tuple>
decltype(auto) apply(F&& f, Tuple&& t) {
using Indices =
std::make_index_sequence<std::tuple_size<std::decay_t<Tuple>>::value>;
return apply_impl(std::forward<F>(f), std::forward<Tuple>(t), Indices{});
}
// Now, for our example
struct Foo {
Foo() {
std::cout << "constructor" << std::endl;
}
Foo(Foo && x) {
std::cout << "move" << std::endl;
}
Foo(Foo const & x) {
std::cout << "copy" << std::endl;
}
~Foo() {
std::cout << "destructor" << std::endl;
}
Foo & operator = (Foo && x) {
std::cout << "move assignment" << std::endl;
return *this;
}
Foo & operator = (Foo & x) {
std::cout << "copy assignment" << std::endl;
return *this;
}
};
std::tuple <Foo,Foo> f(Foo && x,Foo && y) {
std::cout << "Called f" << std::endl;
return std::make_tuple <Foo,Foo> (std::move(x),std::move(y));
}
std::tuple <Foo,Foo> g(Foo && x,Foo && y) {
std::cout << "Called g" << std::endl;
return std::make_tuple <Foo,Foo> (std::move(x),std::move(y));
}
int main() {
Foo x,y;
std::tie(x,y) = f(std::move(x),std::move(y));
std::cout << "Finished with f(foo)" << std::endl;
std::tie(x,y) = apply(g,f(std::move(x),std::move(y)));
std::cout << "Finished with g(f(foo))" << std::endl;
}
这产生
constructor
constructor
Called f
move
move
move assignment
move assignment
destructor
destructor
Finished with f(foo)
Called f
move
move
Called g
move
move
move assignment
move assignment
destructor
destructor
destructor
destructor
Finished with g(f(foo))
destructor
destructor
基本上,出现与上述相同的问题:我们得到移动分配,如果它们消失就好了。
根据@MooingDuck 的建议,实际上可以从函数返回一个 rref。通常,这将是一个非常糟糕的主意,但由于内存是在函数外部分配的,因此它不是问题。然后,移动次数急剧减少。不幸的是,如果有人试图将结果分配给 rref,这将导致未定义的行为。所有代码和结果如下。
对于单参数情况:
#include <iostream>
struct Foo {
// Add some data to see if it gets moved correctly
int data;
Foo() : data(0) {
std::cout << "default constructor" << std::endl;
}
Foo(int const & data_) : data(data_) {
std::cout << "constructor" << std::endl;
}
Foo(Foo && x) {
data = x.data;
std::cout << "move" << std::endl;
}
Foo(Foo const & x) {
data = x.data;
std::cout << "copy" << std::endl;
}
~Foo() {
std::cout << "destructor" << std::endl;
}
Foo & operator = (Foo && x) {
data = x.data;
std::cout << "move assignment" << std::endl;
return *this;
}
Foo & operator = (Foo & x) {
data = x.data;
std::cout << "copy assignment" << std::endl;
return *this;
}
};
Foo && f(Foo && foo) {
std::cout << "Called f: foo.data = " << foo.data << std::endl;
return std::move(foo);
}
Foo && g(Foo && foo) {
std::cout << "Called g: foo.data = " << foo.data << std::endl;
return std::move(foo);
}
int main() {
Foo foo(5);
foo = f(std::move(foo));
std::cout << "Finished with f(foo)" << std::endl;
foo = g(f(std::move(foo)));
std::cout << "Finished with g(f(foo))" << std::endl;
Foo foo2 = g(f(std::move(foo)));
std::cout << "Finished with g(f(foo)) a second time" << std::endl;
std::cout << "foo2.data = " << foo2.data << std::endl;
// Now, break it.
Foo && foo3 = g(f(Foo(4)));
// Notice that the destuctor for Foo(4) occurs before the following line.
// That means that foo3 points at destructed memory.
std::cout << "foo3.data = " << foo3.data << ". If there's a destructor"
" before this line that'd mean that this reference is invalid."
<< std::endl;
}
这产生
constructor
Called f: foo.data = 5
move assignment
Finished with f(foo)
Called f: foo.data = 5
Called g: foo.data = 5
move assignment
Finished with g(f(foo))
Called f: foo.data = 5
Called g: foo.data = 5
move
Finished with g(f(foo)) a second time
foo2.data = 5
constructor
Called f: foo.data = 4
Called g: foo.data = 4
destructor
foo3.data = 4. If there's a destructor before this line that'd mean that this reference is invalid.
destructor
destructor
在多参数情况下
#include <tuple>
#include <iostream>
#include <utility>
// This comes from the N3802 proposal for C++
template <typename F, typename Tuple, size_t... I>
decltype(auto) apply_impl(F&& f, Tuple&& t, std::index_sequence<I...>) {
return std::forward<F>(f)(std::get<I>(std::forward<Tuple>(t))...);
}
template <typename F, typename Tuple>
decltype(auto) apply(F&& f, Tuple&& t) {
using Indices =
std::make_index_sequence<std::tuple_size<std::decay_t<Tuple>>::value>;
return apply_impl(std::forward<F>(f), std::forward<Tuple>(t), Indices{});
}
// Now, for our example
struct Foo {
// Add some data to see if it gets moved correctly
int data;
Foo() : data(0) {
std::cout << "default constructor" << std::endl;
}
Foo(int const & data_) : data(data_) {
std::cout << "constructor" << std::endl;
}
Foo(Foo && x) {
data = x.data;
std::cout << "move" << std::endl;
}
Foo(Foo const & x) {
data = x.data;
std::cout << "copy" << std::endl;
}
~Foo() {
std::cout << "destructor" << std::endl;
}
Foo & operator = (Foo && x) {
std::cout << "move assignment" << std::endl;
return *this;
}
Foo & operator = (Foo & x) {
std::cout << "copy assignment" << std::endl;
return *this;
}
};
std::tuple <Foo&&,Foo&&> f(Foo && x,Foo && y) {
std::cout << "Called f: (x.data,y.data) = (" << x.data << ',' <<
y.data << ')' << std::endl;
return std::tuple <Foo&&,Foo&&> (std::move(x),std::move(y));
}
std::tuple <Foo&&,Foo&&> g(Foo && x,Foo && y) {
std::cout << "Called g: (x.data,y.data) = (" << x.data << ',' <<
y.data << ')' << std::endl;
return std::tuple <Foo&&,Foo&&> (std::move(x),std::move(y));
}
int main() {
Foo x(5),y(6);
std::tie(x,y) = f(std::move(x),std::move(y));
std::cout << "Finished with f(x,y)" << std::endl;
std::tie(x,y) = apply(g,f(std::move(x),std::move(y)));
std::cout << "Finished with g(f(x,y))" << std::endl;
std::tuple <Foo,Foo> x_y = apply(g,f(std::move(x),std::move(y)));
std::cout << "Finished with g(f(x,y)) a second time" << std::endl;
std::cout << "(x.data,y.data) = (" << std::get <0>(x_y).data << ',' <<
std::get <1> (x_y).data << ')' << std::endl;
// Now, break it.
std::tuple <Foo&&,Foo&&> x_y2 = apply(g,f(Foo(7),Foo(8)));
// Notice that the destuctors for Foo(7) and Foo(8) occur before the
// following line. That means that x_y2points at destructed memory.
std::cout << "(x2.data,y2.data) = (" << std::get <0>(x_y2).data << ',' <<
std::get <1> (x_y2).data << ')' << ". If there's a destructor"
" before this line that'd mean that this reference is invalid."
<< std::endl;
}
这产生
constructor
constructor
Called f: (x.data,y.data) = (5,6)
move assignment
move assignment
Finished with f(x,y)
Called f: (x.data,y.data) = (5,6)
Called g: (x.data,y.data) = (5,6)
move assignment
move assignment
Finished with g(f(x,y))
Called f: (x.data,y.data) = (5,6)
Called g: (x.data,y.data) = (5,6)
move
move
Finished with g(f(x,y)) a second time
(x.data,y.data) = (5,6)
constructor
constructor
Called f: (x.data,y.data) = (7,8)
Called g: (x.data,y.data) = (7,8)
destructor
destructor
(x2.data,y2.data) = (7,8). If there's a destructor before this line that'd mean that this reference is invalid.
destructor
destructor
destructor
destructor
最佳答案
在我看来,您需要的不是一种通过函数调用来回移动值的机制,因为引用足以做到这一点,而是一种以这种方式组合函数的设备。
template <void f(Foo &), void g(Foo &)>
void compose2(Foo &v){
f(v);
g(v);
}
当然,您可以在参数类型上使其更通用。
template <typename T, void f(T&), void (...G)(T&)>
void compose(T &v){
f(v);
compose2<T,G...>(v);
}
template <typename T>
void compose(Foo &){
}
例子:
#include <iostream>
//... above template definitions for compose elided
struct Foo {
int x;
};
void f(Foo &v){
v.x++;
}
void g(Foo &v){
v.x *= 2;
}
int main(){
Foo v = { 9 };
compose<Foo, f, g, f, g>(v);
std::cout << v.x << "\n"; // output "42"
}
请注意,您甚至可以在过程原型(prototype)上参数化模板,但此时在我的机器上,似乎只有 clang++ (v3.5) 接受它,g++ (4.9.1) 不喜欢它。
关于c++ - 当复制 elison 失败时,有没有办法阻止移动构造函数后跟移动赋值运算符?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28412305/
我的代码目前看起来像这样numbers=[1,2,3,4,5]defpop_threepop=[]3.times{pop有没有办法在一行中完成pop_three方法中的内容?我基本上想做类似numbers.slice(0,3)的事情,但要删除切片中的数组项。嗯...嗯,我想我刚刚意识到我可以试试slice! 最佳答案 是numbers.pop(3)或者numbers.shift(3)如果你想要另一边。 关于ruby-多次弹出/移动ruby数组,我们在StackOverflow上找到一
我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server
我想在一个没有Sass引擎的类中使用Sass颜色函数。我已经在项目中使用了sassgem,所以我认为搭载会像以下一样简单:classRectangleincludeSass::Script::FunctionsdefcolorSass::Script::Color.new([0x82,0x39,0x06])enddefrender#hamlengineexecutedwithcontextofself#sothatwithintemlateicouldcall#%stop{offset:'0%',stop:{color:lighten(color)}}endend更新:参见上面的#re
请帮助我理解范围运算符...和..之间的区别,作为Ruby中使用的“触发器”。这是PragmaticProgrammersguidetoRuby中的一个示例:a=(11..20).collect{|i|(i%4==0)..(i%3==0)?i:nil}返回:[nil,12,nil,nil,nil,16,17,18,nil,20]还有:a=(11..20).collect{|i|(i%4==0)...(i%3==0)?i:nil}返回:[nil,12,13,14,15,16,17,18,nil,20] 最佳答案 触发器(又名f/f)是
我正在尝试用ruby中的gsub函数替换字符串中的某些单词,但有时效果很好,在某些情况下会出现此错误?这种格式有什么问题吗NoMethodError(undefinedmethod`gsub!'fornil:NilClass):模型.rbclassTest"replacethisID1",WAY=>"replacethisID2andID3",DELTA=>"replacethisID4"}end另一个模型.rbclassCheck 最佳答案 啊,我找到了!gsub!是一个非常奇怪的方法。首先,它替换了字符串,所以它实际上修改了
我有一些代码在几个不同的位置之一运行:作为具有调试输出的命令行工具,作为不接受任何输出的更大程序的一部分,以及在Rails环境中。有时我需要根据代码的位置对代码进行细微的更改,我意识到以下样式似乎可行:print"Testingnestedfunctionsdefined\n"CLI=trueifCLIdeftest_printprint"CommandLineVersion\n"endelsedeftest_printprint"ReleaseVersion\n"endendtest_print()这导致:TestingnestedfunctionsdefinedCommandLin
当我在我的Rails应用程序根目录中运行rakedoc:app时,API文档是使用/doc/README_FOR_APP作为主页生成的。我想向该文件添加.rdoc扩展名,以便它在GitHub上正确呈现。更好的是,我想将它移动到应用程序根目录(/README.rdoc)。有没有办法通过修改包含的rake/rdoctask任务在我的Rakefile中执行此操作?是否有某个地方可以查找可以修改的主页文件的名称?还是我必须编写一个新的Rake任务?额外的问题:Rails应用程序的两个单独文件/README和/doc/README_FOR_APP背后的逻辑是什么?为什么不只有一个?
我在Rails应用程序中使用CarrierWave/Fog将视频上传到AmazonS3。有没有办法判断上传的进度,让我可以显示上传进度如何? 最佳答案 CarrierWave和Fog本身没有这种功能;你需要一个前端uploader来显示进度。当我不得不解决这个问题时,我使用了jQueryfileupload因为我的堆栈中已经有jQuery。甚至还有apostonCarrierWaveintegration因此您只需按照那里的说明操作即可获得适用于您的应用的进度条。 关于ruby-on-r
如何在Ruby中按名称传递函数?(我使用Ruby才几个小时,所以我还在想办法。)nums=[1,2,3,4]#Thisworks,butismoreverbosethanI'dlikenums.eachdo|i|putsiend#InJS,Icouldjustdosomethinglike:#nums.forEach(console.log)#InF#,itwouldbesomethinglike:#List.iternums(printf"%A")#InRuby,IwishIcoulddosomethinglike:nums.eachputs在Ruby中能不能做到类似的简洁?我可以只
如何将send与+=一起使用?a=20;a.send"+=",10undefinedmethod`+='for20:Fixnuma=20;a+=10=>30 最佳答案 恐怕你不能。+=不是方法,而是语法糖。参见http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_expressions.html它说Incommonwithmanyotherlanguages,Rubyhasasyntacticshortcut:a=a+2maybewrittenasa+=2.你能做的最好的事情是: