今天无意中发现了一段代码,我不明白。请考虑以下示例:
#include <iostream>
#include <string>
class A
{
public:
template <class Type>
Type& operator=(Type&& theOther)
{
text = std::forward<Type>(theOther).text;
return *this;
}
private:
std::string text;
};
class B
{
public:
B& operator=(B&& theOther)
{
text = std::forward<B>(theOther).text;
return *this;
}
private:
std::string text;
};
int main()
{
A a1;
A a2;
a2 = a1;
B b1;
B b2;
b2 = b1;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
编译时,MinGW-w64/g++ 10.2 指出:
..\src\Main.cpp: In function 'int main()':
..\src\Main.cpp:41:7: error: use of deleted function 'B& B::operator=(const …Run Code Online (Sandbox Code Playgroud) 在我身上发生了一些我认为完全合理的事情,但是我想要人们对它的看法,以防我完全遗漏某些东西.首先,我的理解T& operator=(T&& rhs)是,当我们完成时,我们不关心内容rhs是什么,只是内容已被移入this并且rhs可以安全地破坏.
话虽这么说,假设交换很便宜,复制赋值运算符的常见异常安全实现看起来像这样:
T& operator=(const T& rhs) {
if(this != &rhs) {
T(rhs).swap(*this);
}
return *this;
}
Run Code Online (Sandbox Code Playgroud)
因此,实现移动赋值运算符的一种自然方式是这样的:
T& operator=(T&& rhs) {
if(this != &rhs) {
T(std::move(rhs)).swap(*this);
}
return *this;
}
Run Code Online (Sandbox Code Playgroud)
但它发生在我身上,rhs不一定是空的!那么,为什么不做一个平原swap呢?
T& operator=(T&& rhs) {
rhs.swap(*this); // is this good enough?
return *this;
}
Run Code Online (Sandbox Code Playgroud)
我认为这满足了移动赋值操作符需要做的...但就像我说的那样,这只是发生在我身上,所以我认为我可能会遗漏一些东西.
我能想到的唯一"缺点"是,this与执行move-construct/swap的版本相比,拥有的东西使用普通交换可能会延长寿命.
思考?
我只是初学者在c ++ 11中的移动操作,所以玩它.但发现了一些我无法理解的东西.
#include <iostream>
using namespace std;
class A{
public:
A(){cout << "default ctor" << endl;}
A(const string& str):_str{str}{cout << "parameter ctor" << endl;}
A(A&& obj):_str{std::move(obj._str)}{cout << "move ctor" << endl;}
A& operator =(A&& rhs){_str = std::move(rhs._str);cout << "move assignment operation" << endl; return *this;}
void print(){cout << _str << endl;}
private:
string _str;
};
int main(){
A a("rupesh yadav"); // parameter ctor
A b(std::move(a)); // move ctor
cout << "print a: ";
a.print(); // NOT printing --> CORRECT!! …Run Code Online (Sandbox Code Playgroud) 我(错误地)在程序中进行了以下分配:
std::shared_ptr<SI::Program> m_program; // in class
m_program = std::make_unique<SI::Program>(); // in method
Run Code Online (Sandbox Code Playgroud)
当我找到这个时,我首先想知道为什么它甚至可以编译。事实证明shared_ptr,unique_ptr对象具有特殊的移动分配运算符。
我的问题是,这样做是否总是安全的,或者有任何含义?
(对于代码执行而言是安全的;对于代码审阅显然不安全...)
想象一下管理资源的以下类(我的问题只是关于移动赋值运算符):
struct A
{
std::size_t s;
int* p;
A(std::size_t s) : s(s), p(new int[s]){}
~A(){delete [] p;}
A(A const& other) : s(other.s), p(new int[other.s])
{std::copy(other.p, other.p + s, this->p);}
A(A&& other) : s(other.s), p(other.p)
{other.s = 0; other.p = nullptr;}
A& operator=(A const& other)
{A temp = other; std::swap(*this, temp); return *this;}
// Move assignment operator #1
A& operator=(A&& other)
{
std::swap(this->s, other.s);
std::swap(this->p, other.p);
return *this;
}
// Move assignment operator #2
A& operator=(A&& other)
{
delete [] p; …Run Code Online (Sandbox Code Playgroud) 我有一个简单的RAII包装器来管理某个资源.这是界面:
struct ResourceWrapper
{
explicit ResourceWrapper(RESOURCE resource);
ResourceWrapper(const ResourceWrapper& other);
ResourceWrapper& operator=(const ResourceWrapper& other);
~ResourceWrapper();
ResourceWrapper(ResourceWrapper&& other) = delete;
ResourceWrapper& operator=(ResourceWrapper&& other) = delete;
};
Run Code Online (Sandbox Code Playgroud)
这里的问题是,std一旦我明确删除了移动赋值运算符,我就不能再将这个类与容器和算法一起使用了.显然我确实需要删除或正确实现它,因为我刚刚学到了很多困难.
另一种方法是通过常规赋值运算符实现移动赋值,但我不确定如何正确执行.我想我需要类似的东西std::remove_reference?我想知道它是否会删除太多的引用,并产生不必要的临时对象.
我遇到一个问题,gcc编译器将局部变量(非临时)作为函数的rvalue参数移动.我有一个简单的例子:
class A
{
public:
A() {}
A& operator=(const A&) { std::cout << "const A&\n"; return *this; }
A& operator=(A&&) { std::cout << "A&&\n"; return *this; }
};
class B
{
public:
B() {}
B& operator=(const B&) { std::cout << "const B&\n"; return *this; }
B& operator=(B&&) { std::cout << "B&&\n"; return *this; }
template<class T> B& operator=(const T&) { std::cout << "const T& (T is " << typeid(T).name() << ")\n"; return *this; }
template<class T> B& operator=(T&&) { std::cout …Run Code Online (Sandbox Code Playgroud) c++ templates lvalue-to-rvalue move-assignment-operator c++14
在容器中具有这些特征的原因是什么(https://en.cppreference.com/w/cpp/memory/allocator_traits)
propagate_on_container_copy_assignment Alloc::propagate_on_container_copy_assignment if present, otherwise std::false_type
propagate_on_container_move_assignment Alloc::propagate_on_container_move_assignment if present, otherwise std::false_type
propagate_on_container_swap Alloc::propagate_on_container_swap if present, otherwise std::false_type
Run Code Online (Sandbox Code Playgroud)
is_always_equal(since C++17) Alloc::is_always_equal if present, otherwise std::is_empty<Alloc>::type
Run Code Online (Sandbox Code Playgroud)
我知道容器实现在分配和交换的实现中会以一种或另一种方式表现。(并且处理这些情况是可怕的代码。)我也明白有时人们可能需要将移动容器保持在一种状态resizeble或者至少可以调用一些最后的释放,因此分配器不能无效。(我个人认为这是一个弱论点。)
但问题是, 为什么这些信息不能成为自定义分配器类型本身的正常实现和语义的一部分?
我的意思是,容器复制分配可以尝试复制分配源分配器,如果语法复制分配没有真正复制,那么,就像说你的容器没有 propagate_on_container_copy_assignment。
以同样的方式而不是使用 is_always_equal一个实际上可以使分配器分配什么也不做。
(此外,如果is_always_equal为真,则可以让operator==分配器返回std::true_type以发出信号。)
在我看来,这些特征似乎试图覆盖可以通过普通 C++ 方式提供给自定义分配器的语义。这似乎与泛型编程和当前的 C++ 哲学背道而驰。
唯一的原因,我认为这对于实现与“旧”容器的某种向后兼容性很有用。
如果我今天要编写一个新容器和/或一个新的非平凡分配器,我可以依靠分配器的语义而忘记这些特征吗?
在我看来,只要移动的分配器可以“解除分配”一个空指针状态(这意味着在这种特殊情况下主要是什么都不做),那么它应该没问题,如果resize抛出,那也很好(有效) ,这只是意味着分配器无法再访问其堆。
编辑:实际上, 我可以这样简单地编写容器吗?并将复杂性委托给自定义分配器的语义?:
templata<class Allocator>
struct my_container{
Allocator alloc_;
...
my_container& operator=(my_container const& other){ …Run Code Online (Sandbox Code Playgroud) allocator move-semantics copy-assignment c++11 move-assignment-operator
首先,我真的检查了是否已经有人问过一个问题,但我找不到任何问题。错误消息不应该欺骗你我的情况有点不同我猜或者我只是错过了一些东西。
当我处理一个玩具 C++ 代码时,我遇到了一个奇怪的错误。程序输出说有双重空闲情况,但我看不到发生此错误的地方。代码可能看的有点长,对此我深表歉意。
我现在正在工作Linux Distribution,我正在使用g++ 9.1.0. 我检查了我的代码并寻找错误的部分。
即使我固定的一些代码的一部分,我的问题没有得到解决,当我发表评论或者除了Foo{1, "Hello World"};或者vec.push_back(std::move(Foo{}));,我不为什么得到它。
class Foo
{
public:
Foo()
: val{nullptr}, str{nullptr}
{
std::cout << "You are in empty constructor\n";
}
Foo(int the_val, const char *the_str)
: val{new int}, str{new char[std::strlen(the_str + 1)]}
{
*val = the_val;
std::cout << *val << '\n';
std::strcpy(str, the_str);
std::cout << str << '\n';
}
~Foo()
{
if (val) {
delete val;
} else {
std::cout << "val is empty\n"; …Run Code Online (Sandbox Code Playgroud) c++ move-constructor construct copy-assignment move-assignment-operator
c++ ×9
c++11 ×6
move ×3
templates ×2
allocator ×1
c++14 ×1
construct ×1
shared-ptr ×1
std ×1
unique-ptr ×1