C ++统一分配操作符移动语义 [英] C++ Unified Assignment Operator move-semantics

查看:156
本文介绍了C ++统一分配操作符移动语义的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

编辑:解决了查看评论
- 不知道如何标记为解决了一个答案。



在c ++ 0x中的完美转发/移动语义是一个什么导致相信这是一个写一个新的赋值运算符的好方法。

  #include< string> 
#include< vector>
#include< iostream>

struct my_type
{
my_type(std :: string name_)
:name(name_)
{}

my_type(const my_type&)= default;

my_type(my_type&& other)
{
this-> swap(other);
}

my_type& operator =(my_type other)
{
swap(other);
return * this;
}

void swap(my_type& other)
{
name.swap(other.name);
}

private:
std :: string name;
void operator =(const my_type&)= delete;
void operator =(my_type&&)= delete;
};


int main()
{
my_type t(hello world);
my_type t1(foo bar);
t = t1;
t = std :: move(t1);
}

这应该允许r值和const& s分配给它。通过用适当的构造函数构造一个新对象,然后用*交换内容。这似乎听起来很正常,因为没有数据被复制超过它需要。和指针运算是便宜。



但是,我的编译器不同意。 (g ++ 4.6)我得到这些错误。

  copyconsttest.cpp:在函数'int main b copyconsttest.cpp:40:4:错误:'t = t1'中的'operator ='的模糊重载
copyconsttest.cpp:40:4:注意:候选项为:
copyconsttest.cpp:18 :11:note:my_type& my_type :: operator =(my_type)
copyconsttest.cpp:30:11:note:my_type& my_type :: operator =(const my_type&)< deleted>
copyconsttest.cpp:31:11:note:my_type& my_type :: operator =(my_type&&)< near match>
copyconsttest.cpp:31:11:注意:没有已知的从'my_type'到'my_type&'
的拷贝转换。copyconsttest.cpp:41:16:error: ='in't = std :: move [with _Tp = my_type& typename std :: remove_reference< < template-parameter-1-1> > :: type = my_type]((*& t1))'
copyconsttest.cpp:41:16:note:candidate are:
copyconsttest.cpp:18:11:note:my_type& my_type :: operator =(my_type)
copyconsttest.cpp:30:11:note:my_type& my_type :: operator =(const my_type&)< deleted>
copyconsttest.cpp:31:11:note:my_type& my_type :: operator =(my_type&&)< deleted>

我做错了什么?这是不好的做法(我不认为有办法测试你是否自我分配)?是编译器还没准备好吗?



感谢

解决方案

非常leery的副本/交换分配习语。这可能是次优的,特别是当不进行仔细分析时。



对于你的例子,我推荐:

  struct my_type 
{
my_type(std :: string name_)
:name(name_)
{}

void swap(my_type& other)
{
name.swap(other.name);
}

private:
std :: string name;
};

这将获得隐式复制和移动语义,转发到std :: string的复制和移动成员。 std :: string的作者知道如何完成这些操作。



如果你的编译器还不支持隐式移动生成,但是支持默认的特殊成员,您可以改为:

  struct my_type 
{
my_type(std :: string name_)
:name(name_)
{}

my_type(const mytype&)= default;
my_type& operator =(const mytype&)= default;
my_type(mytype&&)= default;
my_type& operator =(mytype&&)= default;

void swap(my_type& other)
{
name.swap(other.name);
}

private:
std :: string name;
};

如果您只想简单介绍特殊成员,您也可以选择执行上述操作。 / p>

如果你正在处理一个不支持默认特殊成员(或隐式移动成员)的编译器,那么你可以显式地提供编译器最终默认的编译器成为完全C ++ 11符合:

  struct my_type 
{
my_type(std :: string name_ )
:name(name_)
{}

my_type(const mytype& other)
:name(other.name){}
my_type& operator =(const mytype& other)
{
name = other.name;
return * this;
}
my_type(mytype&& other)
:name(std :: move(other.name)){}
my_type& operator =(mytype&& other)
{
name = std :: move(other.name);
return * this;
}

void swap(my_type& other)
{
name.swap(other.name);
}

private:
std :: string name;
};

如果你真的需要强大的异常安全性来分配,设计一次,包括Luc Danton的建议):

 模板< class C> 
typename std :: enable_if
<
std :: is_nothrow_move_assignable< C> :: value,
C&
> :: type
strong_assign(C& c,C other)
{
c = std :: move
return c;
}

模板< class C>
typename std :: enable_if
<
!std :: is_nothrow_move_assignable< C> :: value,
C&
> :: type
strong_assign(C& c,C other)
{
using std :: swap;
swap(c,other);
return c;现在你的客户可以选择效率(我的类型:: operator =),你可以选择效率(我的类型::运算符=),或强烈的异常安全使用 strong_assign


EDIT: solved see comments --don't know how to mark as solved with out an answer.

After watching a Channel 9 video on Perfect Forwarding / Move semantics in c++0x i was some what led into believing this was a good way to write the new assignment operators.

#include <string>
#include <vector>
#include <iostream>

struct my_type 
{
    my_type(std::string name_)
            :    name(name_)
            {}

    my_type(const my_type&)=default;

    my_type(my_type&& other)
    {
            this->swap(other);
    }

    my_type &operator=(my_type other)
    {
            swap(other);
            return *this;
    }

    void swap(my_type &other)
    {
            name.swap(other.name);
    }

private:
    std::string name;
    void operator=(const my_type&)=delete;  
    void operator=(my_type&&)=delete;
};


int main()
{
    my_type t("hello world");
    my_type t1("foo bar");
    t=t1;
    t=std::move(t1);
}

This should allow both r-values and const& s to assigned to it. By constructing a new object with the appropriate constructor and then swapping the contents with *this. This seems sound to me as no data is copied more than it need to be. And pointer arithmetic is cheap.

However my compiler disagrees. (g++ 4.6) And I get these error.

copyconsttest.cpp: In function ‘int main()’:
copyconsttest.cpp:40:4: error: ambiguous overload for ‘operator=’ in ‘t = t1’
copyconsttest.cpp:40:4: note: candidates are:
copyconsttest.cpp:18:11: note: my_type& my_type::operator=(my_type)
copyconsttest.cpp:30:11: note: my_type& my_type::operator=(const my_type&) <deleted>
copyconsttest.cpp:31:11: note: my_type& my_type::operator=(my_type&&) <near match>
copyconsttest.cpp:31:11: note:   no known conversion for argument 1 from ‘my_type’ to ‘my_type&&’
copyconsttest.cpp:41:16: error: ambiguous overload for ‘operator=’ in ‘t = std::move [with _Tp = my_type&, typename std::remove_reference< <template-parameter-1-1> >::type = my_type]((* & t1))’
copyconsttest.cpp:41:16: note: candidates are:
copyconsttest.cpp:18:11: note: my_type& my_type::operator=(my_type)
copyconsttest.cpp:30:11: note: my_type& my_type::operator=(const my_type&) <deleted>
copyconsttest.cpp:31:11: note: my_type& my_type::operator=(my_type&&) <deleted>

Am I doing something wrong? Is this bad practice (I don't think there is way of testing whether you are self assigning)? Is the compiler just not ready yet?

Thanks

解决方案

Be very leery of the copy/swap assignment idiom. It can be sub-optimal, especially when applied without careful analysis. Even if you need strong exception safety for the assignment operator, that functionality can be otherwise obtained.

For your example I recommend:

struct my_type 
{
    my_type(std::string name_)
            :    name(name_)
            {}

    void swap(my_type &other)
    {
            name.swap(other.name);
    }

private:
    std::string name;
};

This will get you implicit copy and move semantics which forward to std::string's copy and move members. And the author of std::string knows best how to get those operations done.

If your compiler does not yet support implicit move generation, but does support defaulted special members, you can do this instead:

struct my_type 
{
    my_type(std::string name_)
            :    name(name_)
            {}

    my_type(const mytype&) = default;
    my_type& operator=(const mytype&) = default;
    my_type(mytype&&) = default;
    my_type& operator=(mytype&&) = default;

    void swap(my_type &other)
    {
            name.swap(other.name);
    }

private:
    std::string name;
};

You may also choose to do the above if you simply want to be explicit about your special members.

If you're dealing with a compiler that does not yet support defaulted special members (or implicit move members), then you can explicitly supply what the compiler should eventually default when it becomes fully C++11 conforming:

struct my_type 
{
    my_type(std::string name_)
            :    name(name_)
            {}

    my_type(const mytype& other)
        : name(other.name) {}
    my_type& operator=(const mytype& other)
    {
        name = other.name;
        return *this;
    }
    my_type(mytype&& other)
        : name(std::move(other.name)) {}
    my_type& operator=(mytype&& other)
    {
        name = std::move(other.name);
        return *this;
    }

    void swap(my_type &other)
    {
            name.swap(other.name);
    }

private:
    std::string name;
};

If you really need strong exception safety for assignment, design it once and be explicit about it (edit to include suggestion by Luc Danton):

template <class C>
typename std::enable_if
<
    std::is_nothrow_move_assignable<C>::value,
    C&
>::type
strong_assign(C& c, C other)
{
    c = std::move(other);
    return c;
}

template <class C>
typename std::enable_if
<
    !std::is_nothrow_move_assignable<C>::value,
    C&
>::type
strong_assign(C& c, C other)
{
    using std::swap;
    swap(c, other);
    return c;
}

Now your clients can choose between efficiency (my type::operator=), or strong exception safety using strong_assign.

这篇关于C ++统一分配操作符移动语义的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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