首页 > 解决方案 > 当有用户定义的移动赋值运算符时,模板化的移动赋值运算符被删除

问题描述

我有一个带有复制构造函数的类,该构造函数仅在满足条件时才启用,例如在此示例中,当类型参数不是引用时。和一个既不能移动也不能复制的成员(比如互斥锁)。例如(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

标签: c++templatesc++14copy-constructor

解决方案


特殊成员函数(复制/移动)由它们的签名标识。当您尝试对它们进行模板化时,您正在定义具有另一个签名的模板,因为模板参数是模板签名的一部分,并且该功能与您尝试禁止的复制/移动 c'tors 无关。

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

[class.copy]/2

类 X的非模板构造函数是复制构造函数,如果它的第一个参数是 X&、const X&、volatile X& 或 const volatile X&,并且没有其他参数,或者所有其他参数都有默认参数([dcl. fct.default])。

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

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


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

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

推荐阅读