2016-07-15 2 views
7

以下のmanyなどのテンプレート構造体を使用すると、固定されていない可能性のあるオブジェクトを返却し、 ++ 17構造化バインディング(auto [a,b,c] = f();は変数abとcを宣言し、構造体やタプルなどを返すfからその値を割り当てます)。C++用可変長集計(構造体)と構文の返却17可変長テンプレート '建設控除ガイド'

template<typename T1,typename T2,typename T3> 
struct many { 
    T1 a; 
    T2 b; 
    T3 c; 
}; 

// guide: 
template<class T1, class T2, class T3> 
many(T1, T2, T3) -> many<T1, T2, T3>; 

auto f(){ return many{string(),5.7, unmovable()}; }; 

int main(){ 
    auto [x,y,z] = f(); 
} 

としてはstd::tupleが集約初期化をサポートしていない、(Multiple return values (structured bindings) with unmovable types and guaranteed RVO in C++17も、 Do std::tuple and std::pair support aggregate initialization?特にaccepted answer by ecatmur)これらの二つの質問と回答で説明しました。つまり、移動不可能な型を保持して戻すことはできません。しかし、manyのような単純な構造体はこれを行うことができます。

任意の数の引数で動作するmanyの可変版を作成することはできますか?

更新:manyのテンプレートバージョンでは、次のガイド構文を使用できますか?

template<typename Args...>  
many(Args...) -> many<Args...>; 
+0

'auto std :: tie(a、b、c)= ...'のようなsgのどこかに提案があることに注意してください。 – lorro

+0

@lorro IMOそれは悪い考えです:それは、ライブラリ機能としてコア言語機能のポーズを作成します。コンパイラは潜在的な 'using'や名前空間の宇宙などを解決しなければならないことは言うまでもありません。 –

+0

@Revolver_Ocelot:私はそれがもっとうまくいくと思っていますが、いくつかの異なる/追加の理由で、すなわち、それは他の機能と直交していません。これを行う私の好きなやり方は、関数のパラメータの位置を含めて、どこでも変数*を宣言することです。 私が持っているもう1つの問題は、 'a = ...'と書かれているのに対し、 'decltype(a)'の*コンストラクタ*を呼び出すことです。 – lorro

答えて

6

C++では、集約初期化は公開ベースクラスを初期化できます。したがって、継承+パック展開を使用してそのようなクラスを構築することができます。 (今打ち鳴らす3.9でのみ動作します)

//Headers used by "many" class implementation 
#include <utility> 
#include <tuple> 

namespace rw { 
    namespace detail { 

    template <size_t index, typename T> 
    struct many_holder 
    { T value; }; 

    template <typename idx_seq, typename... Types> 
    struct many_impl; 

    template <size_t... Indices, typename... Types> 
    struct many_impl<std::index_sequence<Indices...>, Types...>: many_holder<Indices, Types>... 
    {}; 

    } 

template <typename... Types> 
struct many: detail::many_impl<typename std::make_index_sequence<sizeof...(Types)>, Types...> 
{}; 

template<size_t N, typename... Types> 
auto get(const rw::many<Types...>& data) -> const std::tuple_element_t<N, rw::many<Types...>>& 
{ 
    const rw::detail::many_holder<N, std::tuple_element_t<N, rw::many<Types...>>>& holder = data; 
    return holder.value; 
} 

} 

namespace std { 
    template <typename... Types> 
    struct tuple_size<rw::many<Types...>> : std::integral_constant<std::size_t, sizeof...(Types)> 
    {}; 

    template< std::size_t N, class... Types > 
    struct tuple_element<N, rw::many<Types...> > 
    { using type = typename tuple_element<N, std::tuple<Types...>>::type; }; 
} 

//Headers used for testing 
#include <iostream> 
#include <string> 

int main() 
{ 
    rw::many<int, std::string, int> x = {42, "Hello", 11}; 
    std::cout << std::tuple_size<decltype(x)>() << '\n' << rw::get<1>(x); 
} 

デモ:それは構造化されたバインディングを動作させるために、あなたはタプルインターフェイスを公開する必要があります:std::tuple_sizeを専門とstd::tuple_elementとあなたのクラスのget機能を提供 http://melpon.org/wandbox/permlink/9NBqkcbOuURFvypt

ノート:デモで

  • nth_typeのコメントアウト実装があり、することができますtuple_elementを直接埋め込み、tuple_element<tuple>の実装に延期しないでください。
  • get<many>は、manyのメンバー関数であるか、構造化バインディングが機能するための関連する名前空間に配置する必要があります。 std::getをオーバーロードしないでください(とにかくUBになります)。
  • 私は、非const参照とr値参照のために読者のためのエクササイズとしてgetの実装を残しました。
  • 構造化バインディングとガイドを使用する例はありません。clangはそれらをサポートしていないためです(実際にサポートしているコンパイラはわかりません)。理論的には
    template<typename... Types> many(Types...) -> many<Types...>;で動作するはずです。
+0

良いですが、私の3人のメンバーの例と同様に、このテンプレートの引数を使ってこれをどのように動作させるのかという問題もありました。 'template のようなもの many(Args ...) - > many ;'? –

+0

@ JohanLundberg元のコードが有効であれば、バリデーショナルのテンプレート引数を使うためにあなたの "ガイド"を変更する必要があるでしょう。しかし、元のコードを受け入れるオンラインコンパイラを見つけることができなかったので、私はテストできませんでした。 –

+1

@Revolver_Ocelot:この作業をするには、 'tuple_element'や他のインターフェースに' many'を与えて、構造化バインディングを動作させる必要があります。なぜなら、構造化バインディングは基本クラスでは取得できないからです。また、同じ基本クラスを2回使用することはできませんので、 'Types'に同じ型を持つことはできません。 –

2

ちょうど先日、std-proposalsにdiscussion about thisがありました。

控除ガイドをサポートするコンパイラ(私が知っている)は、最終的な文言はまだありませんが、リチャード・スミスによれば、次の控除ガイドが有効です(正確には:

)。
template<class A, class B> 
struct Agg 
{ 
    A a; 
    B b; 
}; 

template<class A, class B> 
Agg(A a, B b) -> Agg<A, B>; 

Agg agg{1, 2.0}; // deduced to Agg<int, double> 

したがって、集約のためのvariadic deductionガイドも機能し、集約初期化構文で動作するはずです。それは動作しません除外ガイド、除外ガイドなしでは、コンストラクタが必要です。

+2

問題は、メンバがvariadicである型を持つことができないことです。つまり、 'Types varname ... 'のようなことはできません。 –

関連する問題