简体   繁体   English

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

[英]Can I force a default special member function to be noexcept?

The following structure fails to compile under C++11 due to the fact that I have declared the move assignment operator as noexcept : 由于我已将移动赋值运算符声明为noexcept因此以下结构无法在C ++ 11下编译:

struct foo
{
  std::vector<int> data;
  foo& operator=(foo&&) noexcept = default;
};

The default move assignment operator generated by the compiler is noexcept(false) due to the fact that std::vector<int> 's move assignment is also noexcept(false) . 编译器生成的默认移动赋值运算符是noexcept(false)因为std::vector<int>的移动赋值也是noexcept(false) This in turn is due to the fact that the default allocator has std::allocator_traits<T>:: propagate_on_container_move_assignment set to std::false_type . 这反过来是由于默认分配器将std::allocator_traits<T>:: propagate_on_container_move_assignment设置为std::false_type See also this question . 另见这个问题

I believe this has been fixed in C++14 (see library defect 2103 ). 我相信这已在C ++ 14中修复 (参见库缺陷2103 )。

My question is, is there a way for me to force noexcept upon the default move assignment assignment operator without having to define it myself? 我的问题是,有没有办法让我在默认移动赋值赋值运算符上强制noexcept而不必自己定义它?

If this is not possible, is there a way I can trick the std::vector<int> into being noexcept move assignable so that noexcept(true) is passed through to my struct? 如果这是不可能的,有没有办法可以将std::vector<int>欺骗为noexcept move assignable,以便将noexcept(true)传递给我的struct?

I believe this has been fixed in C++14 (see library defect 2103). 我相信这已在C ++ 14中修复(参见库缺陷2103)。

As a DR that fix should be considered a correction to C++11 and so some C++11 implementations will have already fixed it. 作为修复的DR应该被认为是对C ++ 11的修正,因此一些C ++ 11实现已经修复了它。

My question is, is there a way for me to force noexcept upon the default move assignment assignment operator without having to define it myself? 我的问题是,有没有办法让我在默认移动赋值赋值运算符上强制noexcept而不必自己定义它?

For the defaulted move assignment operator to be noexcept you need to make its sub-objects have noexcept move assignment operators. 对于默认的移动赋值运算符是noexcept您需要使其子对象具有noexcept移动赋值运算符。

The most obvious portable way I can think of is to use a wrapper around std::vector which forces the move to be 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;
  };

Another similar option is to define your own allocator type with the DR 2013 fix and use that: 另一个类似的选项是使用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>>;

Another option is to use a standard library implementation such as GCC's which implements the resolution to DR 2013 and also makes std::vector 's move assignment operator noexcept for other allocator types when it is known that all allocator instances compare equal. 另一种选择是使用标准库实现,例如GCC,它实现DR 2013的解析,并且当已知所有分配器实例比较相等时,还使std::vector的移动赋值运算符noexcept用于其他分配器类型。

I do not think you can force anything, but you may wrap it: 我不认为你可以强迫任何东西,但你可以包装它:

#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;
}

(Thanks Jonathan Wakely) (谢谢Jonathan Wakely)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM