添加基于模板参数的拷贝构造函数

Add copy constructor based on template parameters

我有一个类似容器的 class,如果基础类型是只移动的,我希望它是只移动的,否则是可复制的。为简单起见,我们假设可复制性由单个 bool 模板参数决定:

template<bool IsCopyable>
struct Foo
{
    Foo();
    Foo(const Foo&);    // only include this when IsCopyable is true
    Foo(Foo&&);

    Foo& operator=(const Foo&);  // only when IsCopyable
    Foo& operator=(Foo&&);
};

现在,我不能只使用 SFINAE 复制构造函数,因为这需要将其模板化,而模板化函数不能作为复制构造函数。另外,我不能只在复制构造函数中执行 static_assert()。虽然这会捕获复制构造函数的错误使用,但它也使得 class 固有的复制可从外部构造(std::is_copy_constructible 类型特征将产生 true)。

顺便说一下,一个不幸的要求是它需要在 VC++ 2012 中编译,所以我不能使用花哨的表达式 SFINAE,继承 ctors,defaulted/deleted 函数或 constexpr if(尽管如此,如果您对 C++17 有一个巧妙的解决方案,我仍然很想听听它:))

显而易见的方法是使用模板特化。我宁愿不走这条路,因为实际上 Foo 有很多功能,我不想重复自己。尽管如此,这似乎是我唯一的选择,我可以使用基数 class 实现一些代码共享,如下所示:

// Base functionality
template<bool IsCopyable>
struct FooBase
{
    FooBase();

    // move ctor and assignment can go here
    FooBase(FooBase&&);     
    FooBase& operator=(FooBase&&);

    // some generic conversion ctor and assignment that I happen to need
    template<class T> FooBase(T&& t);
    template<class T> FooBase& operator=(T&&);

    // ... all sorts of functionality and datamembers       
};

// Foo<false>
template<bool IsCopyable>
struct Foo : FooBase<IsCopyable>
{
    // can't use inheriting ctors in VS 2012, wrap the calls manually:
    Foo() { }
    Foo(Foo&& other) : FooBase<IsCopyable>(std::move(other)) { }
    Foo& operator=(Foo&& other)
    {
        FooBase<IsCopyable>::operator=(std::move(other));
        return *this;
    }

    template<class T> Foo(T&& t) : FooBase<IsCopyable>(std::forward<T>(t)) { }
    template<class T> Foo& operator=(T&& t)
    {
        FooBase<IsCopyable>::operator=(std::forward<T>(t));
        return *this;
    }
};

// Foo<true>
template<>
struct Foo<true> : FooBase<true>
{
    // add these
    Foo(const Foo&);
    Foo& operator=(const Foo&);

    // wrapping calls because of VS 2012:
    Foo() { }
    Foo(Foo&& other) : FooBase<true>(std::move(other)) { }
    Foo& operator=(Foo&& other)
    {
        FooBase<true>::operator=(std::move(other));
        return *this;
    }

    template<class T> Foo(T&& t) : FooBase<true>(std::forward<T>(t)) { }
    template<class T> Foo& operator=(T&& t)
    {
        FooBase<true>::operator=(std::forward<T>(t));
        return *this;
    }
};

还是有点啰嗦。幸运的是,一旦您可以使用继承构造函数和默认函数,它就会变得更清晰。不过,我希望有一种更简单的方法,最好是不使用基数 class.

struct nonesuch {
private:
    ~nonesuch();
    nonesuch(const nonesuch&);
    void operator=(const nonesuch&);
};

template<bool IsCopyable>
struct Foo {
    Foo(const typename std::conditional<IsCopyable, Foo, nonesuch>::type& other) {
        // copy ctor impl here
    }
private:
    Foo(const typename std::conditional<!IsCopyable, Foo, nonesuch>::type&);
};

同样适用于作业。

我不知道是否可以使用您的 VC++2012 但是...如果我没记错的话,我认为有一种方法比继承自 FooBase 更好一些.

一种自动继承。

您可以声明结构

template <bool>
struct Bar;

并专门化 true 版本所有 constructor/operators

template <>
struct Bar<true>
 {
   Bar ()
    { std::cout << "Bar ()" << std::endl; };

   Bar (Bar &&)
    { std::cout << "Bar (Bar &&)" << std::endl; }

   Bar (Bar const &)
    { std::cout << "Bar (Bar const &)" << std::endl; }

   Bar & operator= (Bar &&)
    { std::cout << "operator= (Bar &&)" << std::endl; return *this; }

   Bar & operator= (Bar const &)
    { std::cout << "operator= (Bar const &)" << std::endl; return *this; }
 };

现在您可以专门化继承自 Bar<true>false 版本,确认 (= default) 您想要的并删除 (= delete) 您不需要的想;像

template <>
struct Bar<false> : public Bar<true>
 {
   using Bar<true>::Bar;

   // confirmed constructor/operators
   Bar ()                   = default;
   Bar (Bar &&)             = default;
   Bar & operator= (Bar &&) = default;

   // deleted constructor/operators
   Bar (Bar const &)             = delete;
   Bar & operator= (Bar const &) = delete;
 };

以下是完整的工作示例

#include <iostream>

template <bool>
struct Bar;

template <>
struct Bar<true>
 {
   Bar ()
    { std::cout << "Bar ()" << std::endl; };

   Bar (Bar &&)
    { std::cout << "Bar (Bar &&)" << std::endl; }

   Bar (Bar const &)
    { std::cout << "Bar (Bar const &)" << std::endl; }

   Bar & operator= (Bar &&)
    { std::cout << "operator= (Bar &&)" << std::endl; return *this; }

   Bar & operator= (Bar const &)
    { std::cout << "operator= (Bar const &)" << std::endl; return *this; }
 };

template <>
struct Bar<false> : public Bar<true>
 {
   using Bar<true>::Bar;

   // confirmed constructor/operators
   Bar ()                   = default;
   Bar (Bar &&)             = default;
   Bar & operator= (Bar &&) = default;

   // deleted constructor/operators
   Bar (Bar const &)             = delete;
   Bar & operator= (Bar const &) = delete;
 };


int main ()
 {
   Bar<true>   bt1;
   Bar<false>  bf1;

   auto bt2 = bt1;
   // auto bf2 = bf1; compilation error (copy constructor deleted)

   auto bt3 = std::move(bt1);
   auto bf3 = std::move(bf1);

   Bar<true>   bt4;
   Bar<false>  bf4;

   bt4 = bt3;
   // bf4 = bf3; compilation error (operator= (Bar const &) deleted)

   bt3 = std::move(bt4);
   bf3 = std::move(bf4);
 }

您不能使用基数 class 来有选择地禁用复制吗?这样你就不需要重复主要 class:

的任何其他功能
template <bool b>
struct MaybeCopyable {};

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

template<bool IsCopyable>
struct Foo : MaybeCopyable<IsCopyable> {
    // other functionality
};

如果MaybeCopyable<false>是基class之一,Foo的复制构造函数将被自动删除。