Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/google-sheets/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 仅仅因为复制可能很昂贵,就应该只移动一个类型吗?_C++_C++11_Move Semantics - Fatal编程技术网

C++ 仅仅因为复制可能很昂贵,就应该只移动一个类型吗?

C++ 仅仅因为复制可能很昂贵,就应该只移动一个类型吗?,c++,c++11,move-semantics,C++,C++11,Move Semantics,我有一个类型是可复制的,但可能是昂贵的复制。我已经实现了move构造函数和move赋值。但我遇到了性能问题,人们在传递值时忘记调用move() 删除copy构造函数,并为实际需要副本的罕见情况提供显式copy()方法,这是一种好的C++11风格吗?这是其他语言的惯用语言(Ruby,JavaScript),但是我不知道C++标准库中禁止任何纯拷贝执行的任何东西。例如,std::vector是可复制的,而std::unique_ptr和std::thread由于其他原因是不可复制的 仅仅因为复制可能

我有一个类型是可复制的,但可能是昂贵的复制。我已经实现了move构造函数和move赋值。但我遇到了性能问题,人们在传递值时忘记调用move()

删除copy构造函数,并为实际需要副本的罕见情况提供显式copy()方法,这是一种好的C++11风格吗?这是其他语言的惯用语言(Ruby,JavaScript),但是我不知道C++标准库中禁止任何纯拷贝执行的任何东西。例如,std::vector是可复制的,而std::unique_ptr和std::thread由于其他原因是不可复制的

仅仅因为复制可能很昂贵,就应该只移动一个类型吗

。如果类型的语义使得复制在概念上有意义,那么使复制可用的正确方法是实现一个复制构造函数,并让用户有机会采用标准语法来调用它:

T a;
T a = b;
如果人们忘记从他们不想再使用的对象中移动。。。好吧,那是他们的缺点:

T c = std::move(a); // I'm doing it right (if I no longer need object a);
T d = b; // If I don't need b anymore, I'm doing it wrong.
如果(出于任何原因)对于您的某些函数,调用方总是希望提供一个可以从中移动的对象,那么让函数接受右值引用:

void foo(my_class&& obj);

my_class a;
foo(a); // ERROR!
foo(std::move(a)); // OK

否。如果该类型可复制,则该类型可复制。这意味着它的复制构造函数可用且有效。这并不意味着有某个成员函数的名称按顺序类似于字符
c
o
p
y
,这“有点类似”。如果复制成本足够高,我会将该类视为不可复制的签名。从语义上讲,只有当你想复制的时候,东西才是可复制的,而昂贵的复制品是决定“不,不可复制”的一个正当理由

复制某些内容的能力并不意味着它需要在可复制的类型中实现。该类型的实现者可以决定它是否应该在语义上可复制

我不会将产生昂贵副本的操作称为“复制”,而是“克隆”或“复制”

您可以这样做:

#include <utility>

template<typename T>
struct DoCopy {
  T const& t;
  DoCopy( T const& t_ ):t(t_) {}
};
template<typename T>
DoCopy<T> do_copy( T const& t ) {
  return t;
}
struct Foo {
  struct ExpensiveToCopy {
    int _[100000000];
  };
  ExpensiveToCopy* data;
  Foo():data(new ExpensiveToCopy()) {}
  ~Foo(){ delete data; }
  Foo(Foo&& o):data(o.data) { o.data = nullptr; }
  Foo& operator=(Foo&& o) { data=o.data; o.data=nullptr; return *this; }
  Foo& operator=(DoCopy<Foo> o) {
    delete data;
    if (o.t.data) {
      data=new ExpensiveToCopy(*o.t.data);
    } else {
      data=new ExpensiveToCopy();
    }
    return *this;
  }
  Foo( DoCopy<Foo> cp ):data(cp.t.data?new ExpensiveToCopy( *cp.t.data ):new ExpensiveToCopy() ) {};
};
int main() {
  Foo one;
  // Foo two = one; // illegal
  Foo three = std::move(one); // legal
  Foo four;
  Foo five = do_copy(three);
  four = std::move(three);
  five = do_copy(four);
}
#包括
模板
结构文档{
T const&T;
DoCopy(T const&T_):T(T_){
};
模板
文件副本(施工和测试){
返回t;
}
结构Foo{
结构费用目录{
整数[100000000];
};
费用副本*数据;
Foo():数据(新ExpensiveToCopy()){}
~Foo(){删除数据;}
Foo(Foo&&o):数据(o.data){o.data=nullptr;}
Foo&operator=(Foo&o){data=o.data;o.data=nullptr;return*this;}
Foo&运算符=(DoCopy o){
删除数据;
if(o.t.数据){
数据=新的费用副本(*o.t.data);
}否则{
数据=新的ExpensiveToCopy();
}
归还*这个;
}
Foo(DoCopy cp):数据(cp.t.data?new ExpensiveToCopy(*cp.t.data):new ExpensiveToCopy()){};
};
int main(){
富一;
//Foo two=1;//非法
Foo three=std::move(一);//合法
福四;
Foo five=复制(三);
四=标准::移动(三);
五份=复印件(四份);
}
这在某种程度上类似于在存在右值引用之前编写类似于
std::move
的语义的方法,也有类似于这种技术的缺点,即语言本身不知道你在搞什么鬼把戏

它的优点是,上述
do_copy
的语法类似于
std::move
的语法,并且它允许您使用传统表达式,而无需创建
Foo
的小实例,然后构造另一个变量的副本等


如果我们希望将其视为可复制的情况很常见(如果要避免的话),我会围绕知道
duplicate
方法的类编写一个复制包装器。

我认为
copy
成员函数的问题在于它会破坏使用赋值运算符的所有模板代码。在某些上下文中,不应通过复制构造复制可复制类型:
virtual T*Clone()const=0
接口类(或非最终实现)表示可复制性,但不能作为复制构造函数实现。现在,
value\u ptr
wrappers可以让你回到复制构造的世界,但是
T
类本身是可复制的,但没有复制构造函数(至少不是公共的)。@Yakk:我不同意。任何标准容器的复制都可能非常昂贵,但它是可复制的。就我个人而言,我相信“复制”的操作有语言定义的语义和语言定义的语法。这两件事应该是一致的。我想你是想评论我的回答,而不是我的评论。:)在任何情况下,在标准容器中都有优先权:虽然转发列表有一个大小,并且可以实现
size()
,但代价很高。因此,标准省略了
size()
,如果你想知道大小,你必须自己计算。@TimCulver:当然不可复制的类型是有原因存在的,如果某些东西不需要复制,那么它就不应该执行它(我也喜欢移动语义使容器更灵活)。我的观点是,如果某些东西需要复制的语义,那么应该通过复制的语法来提供。因此,如果他们禁用复制构造,那么类型是不可复制的。它是可复制的、可克隆的,或者其他一些同义词,但显然不可复制。这是个什么问题?@Yakk:我同意你的分类。这是个问题吗?谁知道呢?取决于你想要什么类型的。但是你肯定不能再称它为可复制的,就许多其他代码而言(例如容器),你的类型永远不会被复制,因为你已经颁布了这样的法令。对,我不反对这种分析。我提议让m