SFINAE 走了一个拷贝构造函数

SFINAE away a copy constructor

在某些情况下,我想 SFINAE 去掉 class 模板的复制构造函数和复制赋值运算符。但是如果我这样做,就会生成一个默认的复制构造函数和一个默认的赋值运算符。 SFINAE 是根据我作为 class 模板参数传递的标签完成的。问题是,SFINAE 仅适用于模板,复制 constructor/assignment 运算符不能作为模板。是否存在解决方法?

从可复制或不可复制的基础派生的方法是此类问题的标准习语(另请参见 Stefan 的评论)。一种实现方式是这样的:

template<bool> struct copyable
{
protected:
  ~copyable() = default;
};

template<> struct copyable<false> 
{
  copyable(copyable const&) = delete;
  copyable&operator=(copyable const&) = delete;
protected:
  ~copyable() = default;
};

template<bool allow_copy>
class some_class : copyable<allow_copy> { /* ... */ };

此解决方案使用有条件不可复制的基础 class(通过明确标记复制构造函数和复制赋值运算符为已删除)。

template <bool>
struct NoCopy;

template <>
struct NoCopy<true>
{
   // C++11 and later: marking as deleted. Pre-C++11, make the copy stuff private.
   NoCopy(const NoCopy&) = delete;
   NoCopy& operator=(const NoCopy&) = delete;
   protected:
      ~NoCopy() = default; // prevent delete from pointer-to-parent
};

template <>
struct NoCopy<false>
{
   // Copies allowed in this case
   protected:
      ~NoCopy() = default; // prevent delete from pointer-to-parent
};

用法示例:

template <typename Number>
struct Foo : NoCopy<std::is_integral<Number>::value>
{
   Foo() : NoCopy<std::is_integral<Number>::value>{}
   {
   }
};

int main()
{
   Foo<double> a;
   auto b = a; // compiles fine
   Foo<int> f;
   auto g = f; // fails!
}

注意:NoCopy 的析构函数被声明为 protected 以避免虚拟继承(感谢提示,@Yakk)。

在C++20中,我们可以使用requires子句来约束特殊的成员函数:

template <typename T>
class C {
public:
    // ...
    C(const C&) requires std::is_copy_constructible_v<T> // for example
    {
        // ...
    }
    C(C&&) requires std::is_move_constructible_v<T> // for example
    {
        // ...
    }
    // ...
};

requires 子句不会使函数成为函数模板,因此这些函数仍然有资格作为特殊成员函数并阻止生成的默认特殊成员函数。你甚至可以有多个,比如说,复制构造函数,只要它们有不同的约束。