当存在用户定义的移动赋值运算符时删除模板化移动赋值运算符

Templated move assignment operator deleted when there is a user defined move assignment operator

我有一个带有复制构造函数的 class,它仅在满足条件时启用,例如在本例中,当类型参数不是引用时。以及一个既不可移动也不可复制的成员(比如互斥锁)。 例如 (https://wandbox.org/permlink/hRx51Ht1klYjN7v5)

#include <iostream>
#include <tuple>
#include <mutex>

using std::cout;
using std::endl;    

template <typename T>
class Something {
public:
    Something() {}

    template <typename Type = T, 
              std::enable_if_t<!std::is_reference<Type>{}>* = nullptr>
    Something(const Something&) {}

    Something& operator=(Something&&) {
        return *this;
    }

    std::mutex mutex_;
};

int main() {
    auto&& one = Something<int>{};
    auto two = one;
    std::ignore = two;
}

当我编译这段代码时,我收到一条错误消息

 copy constructor is implicitly deleted because 'Something<int>' has a user-declared move assignment operator
    Something& operator=(Something&&) {
               ^
1 error generated.

好的,所以我尝试将相同的约束应用于移动赋值运算符

template <typename T>
class Something {
public:
    Something() {}

    template <typename Type = T, 
              std::enable_if_t<!std::is_reference<Type>{}>* = nullptr>
    Something(const Something&) {}

    template <typename Type = T, 
              std::enable_if_t<!std::is_reference<Type>{}>* = nullptr>
    Something& operator=(Something&&) {
        return *this;
    }

    std::mutex mutex_;
};

报错改成这样

copy constructor of 'Something<int>' is implicitly deleted because field 'mutex_' has an inaccessible copy constructor
    std::mutex mutex_;
               ^
1 error generated.

关于如何解决这个问题有什么想法吗?为什么当我明确想要默认构造不可移动不可复制成员时编译器会抱怨?当我删除约束时,编译得很好 https://wandbox.org/permlink/daqWAbF40MyfDJcN

特殊成员函数 (copy/move) 由其签名标识。当您尝试对它们进行模板化时,您正在使用 another 签名定义模板,因为模板参数是模板签名的一部分,并且该函数与 copy/move c' 无关您试图禁止的工具。

为了完整起见,引用标准,强调我的:

[class.copy]/2

A non-template constructor for class X is a copy constructor if its first parameter is of type X&, const X&, volatile X& or const volatile X&, and either there are no other parameters or else all other parameters have default arguments ([dcl.fct.default]).

描述其他特殊成员时出现类似的措辞。它们必须是非模板。

所以您不会影响 SFINAE 的那些功能。而且您不能直接使用 SFINAE 影响它们,因为编译器会确保它们始终存在。影响它们生成或不生成的唯一方法是从基础 类 继承(有条件地,甚至),或拥有成员数据,这将导致它们被定义为已删除。


假设两年内没有任何变化,一线希望是在 C++20 中可以使用 requires 子句约束复制构造函数(或任何特殊成员):

Something(const Something&) requires !std::is_reference_v<T> {}

建议的解决方法(如果我正确理解了要求)是将 copy/move 功能和同步延迟到基础 classes(如果需要可以是 mixins):

#include <iostream>
#include <tuple>
#include <mutex>

using std::cout;
using std::endl;    

// A class for handling synchronisation
struct Synchro
{
    std::mutex mutex_;
};

// a class defining capabilities given a data type
template<class T>
struct DataManager : Synchro
{
    DataManager() {}

    DataManager(DataManager const&);
    DataManager& operator=(DataManager const&);
    DataManager(DataManager&&);
    DataManager& operator=(DataManager&&);

};

// special treatment for references
template<class Ref>
struct DataManager<Ref&> : Synchro
{
    DataManager() {}

    DataManager(DataManager const&) = delete;
    DataManager& operator=(DataManager const&) = delete;
    DataManager(DataManager&&) = delete;
    DataManager& operator=(DataManager&&) = delete;
};

// derive something from the specialised DataManager
template <typename T>
class Something : private DataManager<T>
{
public:
    Something() {}

};

int main() {
    auto&& one = Something<int>{};
    auto two = one;
    std::ignore = two;
}

第一个评论中问题的答案:

Thanks! If you have a template function in Something that is basically a move constructor (but with a template), then when you move an instance of Something in the case where DataManager has the move constructor deleted. Then what happens?

将 move/copy 能力推迟到基础 class 的原因是我们希望从 Something 中完全消除这种担忧。

Can Something delegate construction to the default constructor of DataManager?

What effect does inheriting from a class with a deleted constructor have?

如果你没有在派生class中定义特殊的constructors/destructor(我们故意没有这样做),并且我们在派生class中没有定义任何数据对象,它与将 copy/move 构造函数规则推迟到基础 class 具有相同的效果。这不是完全正确的描述,但实际上是这样。

Does it implicitly delete the corresponding constructor of the derived class?

在这种情况下,是的。

Do the derived class constructors/assignment operators get correspondingly deleted?

在这种情况下有效,是的。

记得在基class中定义所有数据对象。