使用无分配交换有任何明显的缺点吗?

时间:2011-05-27 16:27:28

标签: c++

我正在实施(用于培训目的)冒泡排序模板功能:

template<typename iterInput,
         typename predicate>
void BubbleSort(iterInput first1,iterInput last1,predicate func)
{
    bool swapped(false);
    do
    {
        swapped = false;
        iterInput begin = first1;
        iterInput beginMinus = first1;
        ++begin;
        for (;begin != last1; begin++,beginMinus++)
        {
            if (func(*beginMinus,*begin) )
            {
                std::swap(*beginMinus,*begin);
                swapped = true;
            }
        }
    }
    while(swapped);
}

当我意识到这个函数不能用于没有赋值运算符的类时,就像这个(请原谅我的坏名字):

class NoCopyable
{
public:
    explicit NoCopyable(int value) : value_(value) {}
    NoCopyable(const NoCopyable& other) : value_(other.value_) {}
    ~NoCopyable() {}
    bool operator<(const NoCopyable& other) { return value_ < other.value_; }
    void setValue(int value) { value_ = value; }
    std::ostream& print(std::ostream& os) const { return os << value_; }
private:
    NoCopyable& operator=(const NoCopyable& other);
    int value_;
};

std::ostream& operator<<(std::ostream& os, const NoCopyable& obj)
{
    return obj.print(os);
}

struct PrintNoCopyable
{
    void operator()(const NoCopyable& noCopyable) { std::cout << noCopyable << '\n'; }
};

编译器引发此错误错误1错误C2248:'NoCopyable :: operator =':无法访问类'NoCopyable'中声明的私有成员

所以,我稍微修改代码使用而不是std :: swap函数我的交换函数版本,这里是代码:

template<typename T1,
         typename T2>
void noAssignmentSwap(T1& t1,T2& t2)
{
    T1 temp(t1);
    t1.~T1();
    new (&t1) T1(t2);
    t2.~T2();
    new (&t2) T2(temp);
}

代码编译并给出正确的结果。但是我不完全确定,我记得Sutter的文章建议你避免玩物体的生活时间。这篇文章只是通过玩火而不实际给你任何真正的理由来警告你。如果T1或T2的拷贝构造函数可以抛出,我可以在异常安全中看到问题。但是,如果允许赋值运算符抛出,则标准版本中存在相同的问题。

在这个问题中,你能看到这个版本的交换存在任何可能的缺点吗?

干杯

4 个答案:

答案 0 :(得分:8)

除了其他任何东西,如果一个类没有赋值运算符,它的设计者可能不打算交换它。如果他们这样做了,他们也可能禁用了复制结构,所以你的新交换功能仍然无效。

关于标准库容器不需要赋值的断言 - 只要您不想对它们实际执行任何有用的操作,这是正确的。这段代码是否为你编译?

#include <vector>
using namespace std;

struct A {
    private:
        void operator=( const A &);
};

int main() {
    vector <A> v;
    v.push_back( A() );
    v[0] = A();     // assignment needed here
}

我认为不会。

答案 1 :(得分:6)

不同之处在于,当赋值运算符失败时,您仍然拥有相同数量的对象。

如果您销毁一个对象并且无法创建新对象,则会丢失一个对象!如果它是容器的一部分,容器的状态可能也是无效的。

答案 2 :(得分:4)

你需要一个副本ctor而不是一个赋值运算符,但这两者非常相似,至少在一个典型的情况下,你会有两者,或者你都没有。我不认为这通常可以实现很多。

我将它与xor-swap技巧一起排序:有趣,但通常没用。

答案 3 :(得分:3)

未来的代码维护者可能会感到困惑。