仅当基础类型具有这些构造函数时,才实现模板类型构造函数

时间:2018-01-22 20:47:22

标签: c++ c++11 templates typetraits

有没有办法将类型封装在模板类中(类似于std::optional),它具有所有必需的特殊构造函数和赋值运算符(即复制ctor / assignment,移动ctor /赋值),但仅限于"使"如果基础类型具有这些功能? type_traitsstd::is_copy_constructible的功能看起来可能有所帮助,但我不确定如何使用它们来实现这一目标。作为参考,我试图实现的类型类似于std::optional,但是替代的替代值仅仅是" none"我想使用自定义错误类型。 E.g。

template <typename T>
class ErrorOr {
 public:
  enum class Error {
    FATAL,
    WARNING,
    NONE,
  };

  ErrorOr(T val) : val(val), error(Error::NONE) {}
  ErrorOr(Error error) : error(error) {}

  // TODO: Implement copy/move ctors/assignment operators that only
  // exist if they do for the underlying T

  T get() { val; }

 private:
  T val;
  Error error;
};

这是一个非常简单/最小化的实现,没有很多必要的功能,但希望能说明我想要做的事情。

这在C ++ 11中是否可行?

3 个答案:

答案 0 :(得分:1)

在这种情况下,什么都不做。具有ErrorOr<T>类型成员的T将默认所有特殊成员函数执行正确的操作。如果T不可复制,ErrorOr<T>也不会。

但是,这也不是一个可选类型,因为总是有一个T。如果您最终转移到有条件地具有T的实现, 一种方法是从一个空类型继承,该类型根据需要启用或禁用特殊成员。

简化版本为:

template <bool allowCopies>
struct copy_enabler {
    copy_enabler() = default;
    copy_enabler(copy_enabler const& ) = default;
    copy_enabler& operator=(copy_enabler const& ) = default;
};

template <>
struct copy_enabler<false> {
    copy_enabler() = default;
    copy_enabler(copy_enabler const& ) = delete;
    copy_enabler& operator=(copy_enabler const& ) = delete;
};

然后你可以简单地说:

template <typename T>
class ErrorOr : private copy_enabler</* is T copyable */> { ... };

在实践中,您需要为所有特殊成员函数执行此操作,并添加标记类型,以便在将此技巧用于多个不同的类模板时,它们不会最终共享一个共同的基础。

答案 1 :(得分:0)

在下面的代码中,类X只有在其模板参数属于类类型并且是可复制构造的时才定义了复制构造函数(这意味着它应该具有复制构造函数):

template <typename T>
class X {
  static constexpr bool has_copy_constructor
    = std::is_class<T>::value && std::is_copy_constructible<T>::value;
  struct dummy_type { };
public:
  X() = default;
  X(typename std::conditional<has_copy_constructor,
    const X &, const dummy_type &>::type) { std::cerr << "copy ctor\n"; }
  X(typename std::conditional<has_copy_constructor,
    const dummy_type &, const X &>::type) = delete;
};

int main() {
  X<std::string> x1;
  X<std::string> x2(x1); // OK

  X<int> x3;
  X<int> x4(x3); // ERROR
}

不是我喜欢这个解决方案,但它有点工作。

答案 2 :(得分:-2)

是的,在较新的c ++版本中使用std :: enable_if(std :: enable_if_t,不确定是否在c ++ 11中添加了它)。

template<typename T>
struct my_type
    : T
{
    my_type(T && val, typename std::enable_if<std::is_move_constructible<T>::value>::type * = nullptr)
        : T(std::move(val))
    {
    }
};