2017-11-17 16 views
7

コンテナのようなクラスを持っています。基礎となるタイプがmove-onlyであればmove-onlyですが、それ以外の場合はコピー可能です。それはそれは、テンプレートとテンプレート関数ができない作る必要があるため、私はコピーctorのわずかSFINAEことはできませんが、今テンプレートのパラメータに基づいてコピーコンストラクタを追加する

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&&); 
}; 

:物事をシンプルにするために、のコピー可否は、単一のブールテンプレートパラメータによって決定されたと仮定しましょうコピーctorです。また、私は単にコピーctor内でstatic_assert()を行うことはできません。これはコピーctorの誤った使い方をキャッチしますが、クラスを本質的に外部から構築可能にします(std::is_copy_constructible型の特性は真となります)。

ところで、VC++ 2012でコンパイルする必要があるので、ファンシー表現SFINAEを使用することはできません.Ctors、デフォルト/削除された関数、またはconstexpr ifを継承しています(それにもかかわらず、 C++ 17私はまだそれを聞きたいです:))

明白な方法は、テンプレートの特殊化を使用することです。実際にはFooにはかなりの機能があり、私は自分自身を繰り返すのが好きではないので、私はむしろこのルートにはいきません。それにもかかわらず、これは私の唯一の選択肢に見えます。次のように基本クラスを使用してコードを共有することができます:

// 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; 
    } 
}; 

まだ少し冗長です。幸いにも、継承したctorsとデフォルトの関数を使用することができれば、より洗練されたものになります。それにもかかわらず、基本クラスを使用しない方が理想的です。

答えて

9
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&); 
}; 

同様に割り当てられます。

+0

うわー!シンプルでエレガント。私はこのトリックを覚えておく必要があります。 – max66

+0

C++ 11では、 "private method without definition"イディオムが古くなっている点を除いて、素晴らしい解決策です。これらのメソッドを明示的に削除しました。 –

+0

@ArneVogel OPは削除された機能を使用できないと言います。 –

0

あなたのVC++ 2012で動作するかどうかわかりませんが...私が正しく覚えていれば、より良いと思われる方法があります。FooBaseから継承しています。

一種の自動継承です。

あなたは今、あなたは(= default)を確認し、Bar<true>から継承falseバージョンを特化することができます

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; } 
}; 

trueバージョンを特化することができます何欲しいと削除する(= 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); 
} 
3

は、あなたが選択的にコピーを無効にするには、基本クラスを使用することはできません完全な実施例でありますか?あなたはメインクラスの他の機能のいずれかを繰り返す必要はありませんその方法:MaybeCopyable<false>は、基底クラスの1つである場合

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 
}; 

は、Fooのコピーコンストラクタは自動的に削除されます。

関連する問題