我一直在探索C++ 中Move Constructors的可能性,我想知道在下面的例子中有哪些方法可以利用这个功能.考虑以下代码:
template<unsigned int N>
class Foo {
public:
Foo() {
for (int i = 0; i < N; ++i) _nums[i] = 0;
}
Foo(const Foo<N>& other) {
for (int i = 0; i < N; ++i) _nums[i] = other._nums[i];
}
Foo(Foo<N>&& other) {
// ??? How can we take advantage of move constructors here?
}
// ... other methods and members
virtual ~Foo() { /* no action required */ }
private:
int _nums[N];
};
Foo<5> bar() …Run Code Online (Sandbox Code Playgroud)
考虑以下类,实现移动构造函数的正确方法是什么:
class C {
public:
C();
C(C&& c);
private:
std::string string;
}
Run Code Online (Sandbox Code Playgroud)
当然,这个想法是避免复制string或解除分配两次.
让我们假设基本的例子只是为了清晰,我确实需要一个移动构造函数.
我试过了:
C::C(C&& c) {
//move ctor
string = std::move(c.string);
}
Run Code Online (Sandbox Code Playgroud)
和
C::C(C&& c) : string(std::move(c.string)) {
//move ctor
}
Run Code Online (Sandbox Code Playgroud)
两者都在gcc 4.8上编译正常并运行良好.这似乎选项A是正确的行为,string被复制,而不是使用选项B.移动
这是正确执行的举动构造的?
测试新的Move Semantics.
我刚刚问了一下移动构造函数我遇到的问题.但是,正如评论中所述,问题实际上是"移动分配"操作符和"标准分配"操作符在使用标准的"复制和交换"惯用法时发生冲突.
这是我正在使用的课程:
#include <string.h>
#include <utility>
class String
{
int len;
char* data;
public:
// Default constructor
// In Terms of C-String constructor
String()
: String("")
{}
// Normal constructor that takes a C-String
String(char const* cString)
: len(strlen(cString))
, data(new char[len+1]()) // Allocate and zero memory
{
memcpy(data, cString, len);
}
// Standard Rule of three
String(String const& cpy)
: len(cpy.len)
, data(new char[len+1]())
{
memcpy(data, cpy.data, len);
}
String& operator=(String rhs)
{
rhs.swap(*this);
return *this;
} …Run Code Online (Sandbox Code Playgroud) struct copyable { // and movable
copyable() = default;
copyable(copyable const&) { /*...*/ };
copyable& operator=(copyable const&) { /*...*/ return *this; }
};
Run Code Online (Sandbox Code Playgroud)
由于复制构造函数和复制赋值操作函数是显式定义的,因此它表示编译器不能隐式定义移动构造函数和移动赋值函数,因此不允许移动操作.
能告诉我上述理解是否正确吗?
我一直试图在一个带有boost::optional成员变量的类中定义一个默认的移动构造函数.
#include <boost/optional.hpp>
#include <utility>
#include <vector>
struct bar {std::vector<int> vec;};
struct foo {
foo() = default;
foo(foo&&) = default;
boost::optional<bar> hello;
};
int main() {
foo a;
foo b(std::move(a));
}
Run Code Online (Sandbox Code Playgroud)
我的编译器支持移动语义和默认的移动构造函数,但我不能让它工作.
Run Code Online (Sandbox Code Playgroud)% clang++ foo.cc -std=c++11 -stdlib=libc++ foo.cc:15:7: error: call to deleted constructor of 'foo' foo b(std::move(a)); ^ ~~~~~~~~~~~~ foo.cc:9:3: note: function has been explicitly marked deleted here foo(foo&&) = default; ^ 1 error generated.
有没有办法移动a boost::optional 而不修改Boost的源代码?或者我应该等到Boost支持移动?
我过去遇到了同样的问题boost::any.
考虑以下代码:
#include <iostream>
using namespace std;
void Func(int&& i) {
++i;
}
int main() {
int num = 1234;
cout << "Before: " << num << endl;
Func(std::move(num));
cout << "After: " << num << endl;
}
Run Code Online (Sandbox Code Playgroud)
它的输出是:
Before: 1234
After: 1235
Run Code Online (Sandbox Code Playgroud)
显然,i正在内部进行修改Func,因为它i在被"转换"为r值引用之后被绑定到参数std::move.
好吧,我的观点:
移动对象意味着将资源的所有权从一个对象转移到另一个对象.但是,内置类型不包含资源,因为它们本身就是资源.转移他们持有的资源毫无意义.如示例所示,num修改了s值.它的资源,它的自我,是被修改的资源.
内置类型有移动语义吗?
另外,内置类型对象在移动后(如果是)是一个明确定义的行为吗?
考虑以下因素:
std::string make_what_string( const std::string &id );
struct basic_foo
{
basic_foo( std::string message, std::string id );
};
struct foo
: public basic_foo
{
foo::foo( std::string id)
: basic_foo( make_what_string( id ), std::move( id ) ) // Is this valid?
{
}
};
Run Code Online (Sandbox Code Playgroud)
因为C++中的参数评估顺序是未指定的,所以我想知道是否该行
basic_foo( make_what_string( id ), std::move( id ) )
Run Code Online (Sandbox Code Playgroud)
在上面的代码是有效的.
我知道这std::move只不过是一个演员,但什么时候执行std :: string move ctor?在评估完所有参数之后,是否应该调用基础构造函数?或者这是在评估参数期间完成的?换一种说法:
编译器是否这样做:
std::string &&tmp2 = std::move(id);
std::string tmp1 = make_what_string(id);
basic_foo(tmp1, tmp2);
Run Code Online (Sandbox Code Playgroud)
这是有效的.或这个:
std::string tmp2 = std::move(id);
std::string tmp1 = make_what_string(id);
basic_foo(tmp1, …Run Code Online (Sandbox Code Playgroud) 我基本上想弄清楚,整个"移动语义"概念是一些全新的,还是只是让现有的代码更容易实现?我总是对减少调用复制/构造函数的次数感兴趣,但我通常使用引用(可能是const)传递对象,并确保我总是使用初始化列表.考虑到这一点(并考虑了整个丑陋的&&语法),我想知道是否值得采用这些原则或者只是按照我已经做的编码?这里有什么新东西要做,还是我已经做过的"更容易"的语法糖?
最常见的用法std::forward是,完美地转发转发(通用)引用,例如
template<typename T>
void f(T&& param)
{
g(std::forward<T>(param)); // perfect forward to g
}
Run Code Online (Sandbox Code Playgroud)
这param是一个lvalue,并std::forward最终将它转换为右值或左值,具体取决于与它有关的参数.
看一下cppreference.com的定义,std::forward我发现还有一个rvalue重载
template< class T >
T&& forward( typename std::remove_reference<T>::type&& t );
Run Code Online (Sandbox Code Playgroud)
任何人都可以给我任何rvalue超载的理由吗?我看不到任何用例.如果你想将一个右值传递给一个函数,你可以按原样传递它,不需要std::forward在它上面应用.
这与std::move我不同,我明白为什么人们也想要一个rvalue重载:你可能会处理通用代码,在这些代码中你不知道你传递了什么,你想要无条件支持移动语义,请参阅例如为什么std ::移动采取普遍参考?.
如果编译器可以证明左值不会再次使用,它是否可以进行自动左值到右值转换?这是一个澄清我的意思的例子:
void Foo(vector<int> values) { ...}
void Bar() {
vector<int> my_values {1, 2, 3};
Foo(my_values); // may the compiler pretend I used std::move here?
}
Run Code Online (Sandbox Code Playgroud)
如果将a std::move添加到注释行,则可以将矢量移动到Foo参数中,而不是复制.但是,正如所写,我没有使用std::move.
静态地证明my_values在注释行之后不会被使用,这很容易.那么编译器允许移动向量,还是需要复制它?