参考超载,而不是唯一的传递值+ std :: move? [英] Overload on reference, versus sole pass-by-value + std::move?

查看:77
本文介绍了参考超载,而不是唯一的传递值+ std :: move?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

关于C ++ 0x右值的主要建议似乎是在类中添加move构造函数和move运算符,直到编译器默认实现它们为止。



但是等待如果您使用VC10,这是一个失败的策略,因为在VC10 SP1或最坏的情况下,直到VC11,自动生成可能不会出现。



这是我的问题所在。编写所有这些重复的代码并不有趣。而且令人讨厌。但这对于那些认为很慢的班级来说是一个很好的负担。



:: sighs :: C ++ 0x应该让我编写更少的 em>代码,而不是更多!



然后我有了一个想法。我猜想很多人都有。



为什么不仅仅按价值传递一切? std :: move +复制省略不会使这几乎最佳吗?



示例1-典型的Pre-0x构造函数



  OurClass :: OurClass(const SomeClass& obj):obj(obj){} 

SomeClass o;
OurClass(o); //单个副本
OurClass(s​​td :: move(o)); //单个副本
OurClass(SomeClass()); //单个副本

缺点:



示例2-推荐的C ++ 0x?



  OurClass :: OurClass(const SomeClass& obj):obj(obj){} 
OurClass :: OurClass(SomeClass&&obj):obj(std :: move(obj)){}

SomeClass o;
OurClass(o); //单个副本
OurClass(s​​td :: move(o)); //零份复制,一招
OurClass(SomeClass()); //零份,一招

优点: 可能是最快的。

缺点: 很多代码!



示例3 -传递值+ std :: move



  OurClass :: OurClass(SomeClass obj):obj(std :: move (obj)){} 

SomeClass o;
OurClass(o); //单拷贝,一举
OurClass(s​​td :: move(o)); //零份复制,两步移动
OurClass(SomeClass()); //零份,一招

优点: 没有其他代码。

缺点: 在情况1& 2.如果 SomeClass 没有移动构造函数,则性能将受到极大影响。






<你怎么看?这样对吗?与减少代码的好处相比,招致的损失是否是通常可以接受的损失?

解决方案

我对您的问题感兴趣,因为我是该主题的新手,并进行了一些研究。
让我介绍一下结果。



首先,请您感叹。


:: sighs :: C ++ 0x应该让我写更少的代码,而不是更多!


这也是什么应该是为了让您更好地控制代码。而且确实如此。
我会坚持使用额外的构造函数:

  OurClass :: OurClass(SomeClass&&obj):obj( std :: move(obj)){} 

我个人更喜欢复杂和重要情况下的冗长,因为



例如,以C样式强制转换(T *)pT 和C ++标准 static_cast< T *>(pT)
更详细-但向前迈出了一大步。



第二,我对您的示例3(最后一个测试用例)有点怀疑。我认为可能还涉及另一个移动构造函数,用于从右值创建按值传递参数。因此,我在新的VS2010中创建了一个快速项目,并获得了一些说明。我将在这里发布代码以及结果。



来源:

  // test.cpp:定义控制台应用程序的入口点。 
//

#include stdafx.h

#include< utility>
#include< iostream>

class SomeClass {
mutable int * pVal;
public:
int Val()const {return * pVal; };
SomeClass(int val){
pVal = new int(val);
std :: cout<< SomeClass构造函数(pVal = 0x<< std :: hex<< pVal<< std :: dec<<))<< std :: endl;
}
SomeClass(const SomeClass& r){
pVal = new int(r.Val());
std :: cout<< SomeClass复制构造函数(pVal = 0x<< std :: hex<< pVal<< std :: dec<<))<< std :: endl;
}
SomeClass(const SomeClass& r){
pVal = r.pVal;
r.pVal = 0;
std :: cout<< SomeClass移动构造函数(pVal = 0x<< std :: hex<< pVal<< std :: dec<<))<< std :: endl;
}
〜SomeClass(){
if(pVal)
删除pVal;
std :: cout<< SomeClass析构函数(pVal = 0x<< std :: hex<< pVal<< std :: dec<<))<< std :: endl;
}
};

class OtherClass {
SomeClass sc;
public:
OtherClass(int val):sc(val){
}

请注意以下部分:

  #if 1 
OtherClass(SomeClass r):sc(std: :move(r)){
}
#else
OtherClass(const SomeClass& r):sc(r){
}
OtherClass(const SomeClass&& ; r):sc(std :: move(r)){
}
#endif

...

  int Val(){return sc.Val(); } 
〜OtherClass(){
}
};

#define ECHO(expr)std :: cout<< std :: endl<< 行&l​​t;< __LINE__<< :\t #expr:<< std :: endl; expr

int _tmain(int argc,_TCHAR * argv [])
{
volatile int __dummy = 0;
ECHO(SomeClass o(10));

ECHO(OtherClass oo1(o));
__dummy + = oo1.Val();
ECHO(OtherClass oo2(std :: move(o)));
__dummy + = oo2.Val();
ECHO(OtherClass oo3(SomeClass(20)));
__dummy + = oo3.Val();

ECHO(std :: cout<< __dummy<< std :: endl);
ECHO(返回0);
}

正如您所指出的,有一个编译时开关,它使我能够测试这两种方法。



结果最好在文本比较模式下查看,在左侧您可以看到 #if 1 编译,这意味着我们检查了建议的解决方法,而在右侧- #if 0 ,这意味着我们检查了c ++ 0x中描述的洁净方式!



我错了怀疑编译器做愚蠢的事情;



但是,老实说,我们必须考虑提议的解决方法中另外两个析构函数的调用,但这是确保有一个小缺点,考虑到如果在被破坏的对象上发生移动,则不应执行任何操作。还是很高兴知道。



无论如何,我的意思并不是要在包装类中编写另一个构造函数。这只是几行的问题,因为所有繁琐的工作已经在<$ c $ someClass 中完成了,必须具有移动构造函数。


It seems the main advice concerning C++0x's rvalues is to add move constructors and move operators to your classes, until compilers default-implement them.

But waiting is a losing strategy if you use VC10, because automatic generation probably won't be here until VC10 SP1, or in worst case, VC11. Likely, the wait for this will be measured in years.

Here lies my problem. Writing all this duplicate code is not fun. And it's unpleasant to look at. But this is a burden well received, for those classes deemed slow. Not so for the hundreds, if not thousands, of smaller classes.

::sighs:: C++0x was supposed to let me write less code, not more!

And then I had a thought. Shared by many, I would guess.

Why not just pass everything by value? Won't std::move + copy elision make this nearly optimal?

Example 1 - Typical Pre-0x constructor

OurClass::OurClass(const SomeClass& obj) : obj(obj) {}

SomeClass o;
OurClass(o);            // single copy
OurClass(std::move(o)); // single copy
OurClass(SomeClass());  // single copy

Cons: A wasted copy for rvalues.

Example 2 - Recommended C++0x?

OurClass::OurClass(const SomeClass& obj) : obj(obj) {}
OurClass::OurClass(SomeClass&& obj) : obj(std::move(obj)) {}

SomeClass o;
OurClass(o);            // single copy
OurClass(std::move(o)); // zero copies, one move
OurClass(SomeClass());  // zero copies, one move

Pros: Presumably the fastest.
Cons: Lots of code!

Example 3 - Pass-by-value + std::move

OurClass::OurClass(SomeClass obj) : obj(std::move(obj)) {}

SomeClass o;
OurClass(o);            // single copy, one move
OurClass(std::move(o)); // zero copies, two moves
OurClass(SomeClass());  // zero copies, one move

Pros: No additional code.
Cons: A wasted move in cases 1 & 2. Performance will suffer greatly if SomeClass has no move constructor.


What do you think? Is this correct? Is the incurred move a generally acceptable loss when compared to the benefit of code reduction?

解决方案

I was interested in your question because I was new to the topic and did some research. Let me present the results.

First, your sigh.

::sighs:: C++0x was supposed to let me write less code, not more!

what it is also supposed is to give you a better control over the code. And that it does. I would stick to an extra constructor:

OurClass::OurClass(SomeClass&& obj) : obj(std::move(obj)) {}

I personally prefer verbosity in complex and important situations, because it keeps me and possible readers of my code alerted.

take, for example, the C-style cast (T*)pT and C++ standard static_cast<T*>(pT) Much more verbose - but a big step forward.

Second, I was a bit suspicious about your Example 3, last test case. I thought there might be another move constructor involved to create the passed-by-value parameter from the rvalue. So i have created some quick project in my new VS2010 and got some clarifications. I will post the code here as well as results.

the source:

// test.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"

#include <utility>
#include <iostream>

class SomeClass{
    mutable int *pVal;
public:
    int Val() const { return *pVal; };
    SomeClass(int val){
        pVal = new int(val);
        std::cout << "SomeClass constructor(pVal = 0x" << std::hex << pVal << std::dec << ")" << std::endl;
    }
    SomeClass(const SomeClass& r){
        pVal = new int(r.Val());
        std::cout << "SomeClass copy constructor(pVal = 0x" << std::hex << pVal << std::dec << ")" << std::endl;
    }   
    SomeClass(const SomeClass&& r){
        pVal = r.pVal;
        r.pVal = 0;
        std::cout << "SomeClass move constructor(pVal = 0x" << std::hex << pVal << std::dec << ")" << std::endl;
    }
    ~SomeClass(){
        if(pVal)
            delete pVal;
        std::cout << "SomeClass destructor(pVal = 0x" << std::hex << pVal << std::dec << ")" << std::endl;
    }
};

class OtherClass{
    SomeClass sc;
public:
    OtherClass(int val):sc(val){
    }

Note this secion:

#if 1
    OtherClass(SomeClass r):sc(std::move(r)){
    }
#else
    OtherClass(const SomeClass& r):sc(r){
    }   
    OtherClass(const SomeClass&& r):sc(std::move(r)){
    }
#endif

...

    int Val(){ return sc.Val(); }
    ~OtherClass(){
    }
};

#define ECHO(expr)  std::cout << std::endl << "line " << __LINE__ << ":\t" #expr ":" << std::endl; expr

int _tmain(int argc, _TCHAR* argv[])
{
    volatile int __dummy = 0;
    ECHO(SomeClass o(10));

    ECHO(OtherClass oo1(o));            
    __dummy += oo1.Val();
    ECHO(OtherClass oo2(std::move(o))); 
    __dummy += oo2.Val();
    ECHO(OtherClass oo3(SomeClass(20)));  
    __dummy += oo3.Val();

    ECHO(std::cout << __dummy << std::endl);
    ECHO(return 0);
}

As you have noted, there is a compile-time switch that allows me to test the two approaches.

The results are best viewed in text compare mode, on the left you can see the #if 1 compilation, meaning that we check the proposed workaround, and on the right - #if 0, meaning that we check the "kosher" way described in the c++0x!

I was wrong suspecting the compiler do stupid things; it saved the extra move constructor in the third test case.

But to be honest, we have to account for another two destructors being called in the proposed workaround, but this is for sure a minor drawback taking into account that no actions should be performed if a move has occurred on the object being destructed. Still, it is good to know.

In any case, I am not leaving the point that it is better to write one other constructor in the wrapper class. It is just a matter of several lines, since all the tedious work is already done in the SomeClass that has to have a move constructor.

这篇关于参考超载,而不是唯一的传递值+ std :: move?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆