C++ 我可以强制默认的特殊成员函数为noexcept吗?

C++ 我可以强制默认的特殊成员函数为noexcept吗?,c++,c++11,noexcept,C++,C++11,Noexcept,以下结构无法在C++11下编译,因为我已将移动赋值运算符声明为noexcept: struct foo { std::vector<int> data; foo& operator=(foo&&) noexcept = default; }; structfoo { std::矢量数据; foo&operator=(foo&&)noexcept=默认值; }; 编译器生成的默认移动赋值运算符是noexcept(false),因为std::vecto

以下结构无法在C++11下编译,因为我已将移动赋值运算符声明为
noexcept

struct foo
{
  std::vector<int> data;
  foo& operator=(foo&&) noexcept = default;
};
structfoo
{
std::矢量数据;
foo&operator=(foo&&)noexcept=默认值;
};
编译器生成的默认移动赋值运算符是
noexcept(false)
,因为
std::vector
的移动赋值也是
noexcept(false)
。这又是由于默认分配器将
std::allocator\u traits::propagate\u on\u container\u move\u赋值设置为
std::false\u type
。另见

我相信这已经在C++14中修复(请参阅)

我的问题是,有没有一种方法可以让我在不必自己定义的情况下强制默认的move assignment操作符执行
noexcept

如果这是不可能的,是否有一种方法可以使
std::vector
成为
noexcept
move assignable,从而将
noexcept(true)
传递给我的结构

我相信这已经在C++14中得到了修复(参见库缺陷2103)

作为DR,该修复应该被视为对C++11的修正,因此一些C++11实现已经修复了它

我的问题是,有没有一种方法可以让我在不必自己定义的情况下强制默认的move assignment操作符执行
noexcept

要使默认移动分配运算符为
noexcept
,需要使其子对象具有
noexcept
移动分配运算符

我能想到的最明显的可移植方式是在
std::vector
周围使用一个包装器,它强制移动
noexcept

template<typename T, typename A = std::allocator<T>>
  struct Vector : std::vector<T, A>
  {
    using vector::vector;

    Vector& operator=(Vector&& v) noexcept
    {
      static_cast<std::vector<T,A>&>(*this) = std::move(v);
      return *this;
    }
    Vector& operator=(const Vector&) = default;
  };
模板
结构向量:std::Vector
{
使用vector::vector;
向量和运算符=(向量和v)无例外
{
静态_cast(*this)=标准::移动(v);
归还*这个;
}
向量和运算符=(常量向量-)=默认值;
};
另一个类似的选项是使用DR 2013修复程序定义您自己的分配器类型,并使用:

template<typename T>
  struct Allocator : std::allocator<T>
  {
    Allocator() = default;
    template<typename U> Allocator(const Allocator<U>&) { }
    using propagate_on_container_move_assignment  = true_type;
    template<typename U> struct rebind { using other = Allocator<U>; };
  };

template<typename T>
  using Vector = std::vector<T, Allocator<T>>;
模板
结构分配器:std::分配器
{
分配器()=默认值;
模板分配器(常量分配器&){}
在容器上使用propagate\u\u move\u assignment=true\u type;
模板结构重新绑定{using other=Allocator;};
};
模板
使用Vector=std::Vector;

另一种选择是使用标准库实现,如GCC,它实现对DR 2013的解析,并且在已知所有分配器实例比较相等的情况下,为其他分配器类型使用
std::vector
的移动赋值运算符
noexcept

我认为您不能强制执行任何操作,但你可以把它包装起来:

#include <iostream>
#include <vector>

template <typename T>
struct Wrap
{
    public:
    Wrap() noexcept
    {
        new (m_value) T;
    }

    Wrap(const Wrap& other) noexcept
    {
        new (m_value) T(std::move(other.value()));
    }

    Wrap(Wrap&& other) noexcept
    {
        std::swap(value(), other.value());
    }


    Wrap(const T& other) noexcept
    {
        new (m_value) T(std::move(other));
    }

    Wrap(T&& other) noexcept
    {
        new (m_value) T(std::move(other));
    }

    ~Wrap() noexcept
    {
        value().~T();
    }

    Wrap& operator = (const Wrap& other) noexcept
    {
        value() = other.value();
        return *this;
    }

    Wrap& operator = (Wrap&& other) noexcept
    {
        value() = std::move(other.value());
        return *this;
    }

    Wrap& operator = (const T& other) noexcept
    {
        value() = other;
        return *this;
    }

    Wrap& operator = (T&& other) noexcept
    {
        value() = std::move(other);
        return *this;
    }

    T& value() noexcept { return *reinterpret_cast<T*>(m_value); }
    const T& value() const noexcept { return *reinterpret_cast<const T*>(m_value); }
    operator T& () noexcept { return value(); }
    operator const T& () const noexcept { return value(); }

    private:
    typename std::aligned_storage <sizeof(T), std::alignment_of<T>::value>::type m_value[1];
};


struct Foo
{
    public:
    Foo& operator = (Foo&&)  noexcept = default;

    std::vector<int>& data() noexcept { return m_data; }
    const std::vector<int>& data() const noexcept { return m_data; }

    private:
    Wrap<std::vector<int>> m_data;
};

int main() {
    Foo foo;
    foo.data().push_back(1);
    Foo boo;
    boo = std::move(foo);
    // 01
    std::cout << foo.data().size() << boo.data().size() << std::endl;
    return 0;
}
#包括
#包括
模板
结构包裹
{
公众:
Wrap()无例外
{
新(m_值)T;
}
包裹(常量包裹和其他)无例外
{
新的(m_值)T(std::move(other.value());
}
换行(换行和其他)无例外
{
std::swap(value(),other.value());
}
包裹(常数T和其他)无例外
{
新(m_值)T(标准::移动(其他));
}
包装(T和其他)无例外
{
新(m_值)T(标准::移动(其他));
}
~Wrap()不例外
{
值().~T();
}
换行符和运算符=(常量换行符和其他)无例外
{
value()=其他.value();
归还*这个;
}
换行运算符=(换行和其他)无例外
{
value()=std::move(other.value());
归还*这个;
}
换行运算符=(常量T和其他)无例外
{
值()=其他;
归还*这个;
}
换行运算符=(T&&other)无例外
{
value()=std::move(其他);
归还*这个;
}
T&value()noexcept{return*重新解释(m_值);}
const T&value()const noexcept{return*reinterpret_cast(m_value);}
运算符T&()noexcept{return value();}
运算符const T&()const noexcept{return value();}
私人:
typename std::aligned_storage::type m_value[1];
};
结构Foo
{
公众:
Foo&operator=(Foo&&)noexcept=默认值;
std::vector&data()noexcept{return m_data;}
const std::vector&data()const noexcept{return m_data;}
私人:
包装m_数据;
};
int main(){
富富,;
foo.data().向后推_(1);
福布斯;
boo=std::move(foo);
// 01

std::cout如果
data
是一个
std::vector
,而
a
不是默认的
std::allocator
,可以吗?我希望将代码复杂度保持在最低水平。我希望使用自定义分配器比简单的非默认移动赋值运算符更难让其他程序员理解。也就是说,如果
a
std::vector
中的
是默认分配器的简单内联包装器。它可能会工作……如果数据上的移动操作抛出,会发生什么情况?该类型不可复制,您需要复制ctor和赋值op。此外,您可以使用
std::aligned_storage::type
而不是char数组。