2016-09-02 10 views
14

私の目的は、完全には壊れていないかもしれませんが、少なくとも完全に動作するコンストラクタ/デストラクタのペアとstd::get<>()関数を書くことです。std :: variant-likeクラスを実装するときにタイプタグを保存する際の問題

文字配列を使用してメモリを予約しようとしました。それのサイズはfind_biggest_size<>()機能を使用して見つけられる最大のタイプによって決まります。コンストラクタは、型が指定された型のリストにあるかどうかをチェックするため、静的なアサートを使用します。今のところ、コンストラクタとそのコンストラクタが動作します。

template <typename ... alternatives> 
class variant 
{ 
    char object[find_biggest_size<alternatives...>::value]; 
public: 
    template <typename T> 
    variant(T&& other) 
    { 
     static_assert(is_present<T, alternatives...>::value, "type is not in range"); 
     new ((T*)&object[0]) T(std::forward<T>(other)); 
    } 

    template <typename T, typename ... ArgTypes> 
    variant(in_place_t<T>, ArgTypes&& ... args) 
    { 
     static_assert(is_present<T, alternatives...>::value, "type is not in range"); 
     new ((T*)&object[0]) T(std::forward<ArgTypes>(args)...); 
    } 

    ~variant() 
    { 
     // what to do here? 
    } 
}; 

私は問題につまずいた。私はオブジェクトが死ぬときにどのデストラクタを実行するのか分かりません。その上に、適切なタイプを得るためにstd::get<>()を特化することはできないので、基礎となるオブジェクトにアクセスすることは不可能です。

私の質問は:どのようにオブジェクトの作成後に型を格納するのですか?それは正しいアプローチですか?そうでない場合、私は何を使うべきですか?

EDIT:

私はコメントを適用しようとしました。問題は、現在生きている型のインデックスはconstexprにできないため、型リストから必要な型を抽出して適切なデストラクタを呼び出すことができないということです。

~variant() 
{ 
    using T = typename extract<index, alternatives...>::type; 
    (T*)&object[0]->~T(); 
} 

EDIT:

私は、ベースラインの実装を行いました。それは機能しますが、欠けている機能がたくさんあります。あなたはそれを見つけることができますhere。レビューをいただき、うれしく思いますが、最初にhow do I write a good answer?とお読みください。

+0

バリアントがそれで生きているオブジェクトの種類を追跡します。例えば。 [索引](http://en.cppreference.com/w/cpp/utility/variant/index)。 – Columbo

+0

@Columbo、それを試してみましょう。私はis_presentを再実装し、インデックス – Incomputable

+4

を返します。また、['std :: aligned_union' exists](http://en.cppreference.com/w/cpp/types/aligned_union)。 –

答えて

10

どのように私はおそらく起動したい:

#include <iostream> 
#include <utility> 
#include <array> 

template<class...Types> 
struct variant 
{ 
    variant() {} 
    ~variant() 
    { 
     if (type_ >= 0) 
     { 
      invoke_destructor(type_, reinterpret_cast<char*>(std::addressof(storage_))); 
     } 
    } 

    template<class T> static void invoke_destructor_impl(char* object) 
    { 
     auto pt = reinterpret_cast<T*>(object); 
     pt->~T(); 
    } 

    static void invoke_destructor(int type, char* address) 
    { 
     static const std::array<void (*)(char*), sizeof...(Types)> destructors 
     { 
      std::addressof(invoke_destructor_impl<Types>)... 
     }; 
     destructors[type](address); 
    } 

    std::aligned_union_t<0, Types...> storage_; 
    int type_ = -1; 

}; 

int main() 
{ 
    variant<int, std::string> v; 

} 
+1

FYI: 'std :: variant'は"決して空でない "変種です。したがって、値を持たないようにデフォルト設定されません。 –

+1

@ NicolBolasよく分かりませんが、「ほとんど決して空ではありません」と私は理解していますが、デストラクタだけに焦点を当てたいと思っていました。コンストラクタとアクセサは私が思うにそこから続きます。 –

+0

あなたは基本的にデストラクタを列挙しましたか?私はそれについて考えましたが、それを行う方法を見つけることができませんでした。 – Incomputable

0

最初に、どのオブジェクトが現在バリアントにあるかを知る必要があります。その中から型を取得したい場合は、例外をスローする必要があります。

私はユニオンを使用しています(私はhereとするとconstexprとなります)。プレースメントのnew演算子をconstexprとして使用することはできません。そのため、ユニオンがこれを行う唯一の実際の方法だと思います(これは私が思いついた唯一の方法です)。心配します。あなたは依然としてデストラクタを明示的に呼び出す必要があります。 constexprで使用されている型は簡単に破壊可能でなければならないので、私は奇妙な回避策を生み出します。

今すぐ:find_biggest_sizeに似たクラスを実装できます。これは、テンプレートパラメータとしてintの型を指定します。私。その(不完全例)のようなもの:

template<int idx, typename ...Args> 
struct get_type; 

template<int idx, typename First, typename ...Rest> 
struct get_type<idx, First, Rest...> 
{ 
    using type = typename get_type<idx-1, Rest>::type;  
}; 

template<typename First, typename ...Rest> 
struct get_type<0, First, Rest...> 
{ 
    using type = First; 
}; 
//plus specialization without Rest 

そして、あなたが得る機能を実装できます。

template<int i, typename ...Args> 
auto get(variant<Args...> & v) -> typename get_type<i, Args...>::type 
{ /* however you do that */ } 

私はそれが役に立てば幸い。

+2

これは、*ランタイム整数*を取り、どのコンストラクタを呼び出すかを理解する方法を説明していません。 –

関連する問題