c++ - 有条件地禁用复制构造函数

标签 c++ templates constructor sfinae

假设我正在编写一个类模板 C<T>持有 T值,所以 C<T>仅当 T 时才可复制是可复制的。通常,当模板可能支持或可能不支持某个操作时,您只需定义该操作,并由调用者在不安全时避免调用它:

template <typename T>
class C {
 private:
  T t;

 public:
  C(const C& rhs);
  C(C&& rhs);

  // other stuff
};

但是,这在复制构造函数的情况下会产生问题,因为 is_copy_constructible<C<T>>即使 T 也是如此不可复制;如果调用该特征,则该特征看不到复制构造函数的格式错误。 这是一个问题,例如,vector如果 std::is_copy_constructible 有时会避免使用移动构造函数是真的。我该如何解决这个问题?

我相信 is_copy_constructible如果构造函数被显式或隐式默认,则将做正确的事情:

template <typename T>
class C {
 private:
  T t;

 public:
  C(const C& rhs) = default;
  C(C&& rhs) = default;

  // other stuff
};

但是,并非总是可以构造您的类,以便默认构造函数执行正确的操作。

我能看到的另一种方法是使用 SFINAE 有条件地禁用复制构造函数:

template <typename T>
class C {
 private:
  T t;

 public:
  template <typename U = C>
  C(typename std::enable_if<std::is_copy_constructible<T>::value,
                            const U&>::type rhs);
  C(C&& rhs);

  // other stuff
};

除了丑陋之外,这种方法的问题在于我必须将构造函数设为模板,因为 SFINAE 仅适用于模板。根据定义,复制构造函数不是模板,所以我禁用/启用的东西实际上并不是复制构造函数,因此它不会抑制编译器隐式提供的复制构造函数。

我可以通过显式删除复制构造函数来解决这个问题:

template <typename T>
class C {
 private:
  T t;

 public:
  template <typename U = C>
  C(typename std::enable_if<std::is_copy_constructible<T>::value,
                            const U&>::type rhs);
  C(const C&) = delete;
  C(C&& rhs);

  // other stuff
};

但这仍然不能阻止在重载决议期间考虑复制构造函数。这是一个问题,因为在其他条件相同的情况下,普通函数将在重载决议中胜过函数模板,因此当您尝试复制 C<T> 时,普通的复制构造函数被选中,导致构建失败,即使 T是可复制的。

我发现原则上可行的唯一方法是从主模板中省略复制构造函数,并以部分特化的方式提供它(当 T 不可复制时,使用更多的 SFINAE 技巧来禁用它)。但是,这很脆弱,因为它需要我复制 C 的整个定义。 ,这会造成两个拷贝不同步的重大风险。我可以通过让方法体共享代码来缓解这种情况,但我仍然必须复制类定义和构造函数成员初始化列表,这为错误潜入提供了足够的空间。我可以通过让它们都继承来进一步缓解这种情况来自一个公共(public)基类,但引入继承可能会产生各种不受欢迎的后果。此外,当我想要做的只是禁用一个构造函数时,公共(public)继承似乎是适合这项工作的错误工具。

有没有我没有考虑过的更好的选择?

最佳答案

一个值得注意的方法是对周围的类模板进行部分特化。

template <typename T,
          bool = std::is_copy_constructible<T>::value>
struct Foo
{
    T t;

    Foo() { /* ... */ }
    Foo(Foo const& other) : t(other.t) { /* ... */ }
};

template <typename T>
struct Foo<T, false> : Foo<T, true>
{
    using Foo<T, true>::Foo;

    // Now delete the copy constructor for this specialization:
    Foo(Foo const&) = delete;

    // These definitions adapt to what is provided in Foo<T, true>:
    Foo(Foo&&) = default;
    Foo& operator=(Foo&&) = default;
    Foo& operator=(Foo const&) = default;
};

这种方式 is_copy_constructible 的特征恰好在 T is_copy_constructible 的地方得到满足。

关于c++ - 有条件地禁用复制构造函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27073082/

相关文章:

c++ - x的倍数

c++ - 为什么 operator= 返回引用而不是 const 引用

Magento adminhtml 布局更新

C++ 生成标准构造函数

c++ - 需要有关对象/连接池项的 RAII 设计的建议

jQuery 和 html 5 <template> 标签

c++ - 模板化 ostream 重载歧义错误 : basic_ostream<char> vs const char[]

c++ - C++中的复制构造函数

c++ - 存在完美的转发构造函数时,按左值引用转发构造函数的目的是什么?

c++ - 在函数中返回大对象