1

C++ で実装された、いくつかのインプリメンター クラス (impl) とユーザー用のラッパーがあります。impl とラッパーを 2 つの異なるタプルに保持して、1 つの割り当てで impl を初期化できるようにしたいと考えています。(他にも理由があります:)。

問題は、Visual Studio 2012 標準ライブラリのタプル クラスでは、ラッパーの const 参照コピー コンストラクターがないと、ラッパー タプルを構築できないことです。悲しいことに、その場合、次のように const_cast する必要があります。

#include <iostream>
#include <type_traits>
#include <tuple>
#include <typeinfo>

template <typename Member>
struct A
{
    A(Member& m) : member(m)
    { std::cout << typeid(Member).name() << " MMBR " << member << std::endl; }

    A(const Member& m) : member(const_cast<Member&>(m))
    { std::cout << typeid(Member).name() << " CMBR " << member << std::endl; }

    void Print()
    {
        std::cout << typeid(Member).name() << " PRNT " << member << std::endl;
    }

    Member& member;//yes I need to hold this as a mutable reference
};

int main()
{
    typedef std::tuple<A<int>, A<double>, A<short>> WrapperTuple;
    typedef std::tuple<int, double, short> Tuple;

    Tuple t(0, 1, 2);
    WrapperTuple w(t);

    std::get<1>(w).Print();
    return std::cin.get();
}

上記のコードは意図したとおりにコンパイルおよび実行されますが、ラッパー クラス A の const-ref-ctor を削除またはコメント アウトすると、VS2012 コンパイラも gcc4.7.2 コンパイラもコードをコンパイルしません。(1) 私は何を間違っていますか?

私は c++11 の適切なドキュメントを持っていないので、タプルの可変引数コピー ctor は他のタプルの const ref のみを取ると思います。もしそうなら、(2)タプルクラスにそのようなctorがないのはなぜですか?背後にある主な理由を意味します。

要約すると、単一のアクション (つまり、make_shared) で割り当てることができるように、すべての impl とラッパーをタプルにまとめる必要があります。コンパイル時に型で検索できるようにいくつかのヘルパーを作成したため、タプルはやや必須です (例: Get<A<int>>(w)) (3) 各 impl を個別に割り当てる必要がないように、impl への参照を保持するきちんとした方法はありますか? .

4

2 に答える 2

3

のコピー コンストラクターはstd::tuple、変換するものであっても、明らかにすべての要素をコピーしますconst。ほとんどの場合、この動作は完全に合理的です。

特別なケースの回避策は、あなたが望むものよりも少し複雑ですが、うまくいきます。基本的な考え方は、概念的にはタプルをコピーしたくないが、その要素を他のタプルの要素への初期化子として使用したいということですconst

template<unsigned...> struct seq{};
template<unsigned N, unsigned... Is>
struct gen_seq : gen_seq<N-1, N-1, Is...>{};
template<unsigned... Is>
struct gen_seq<0, Is...> : seq<Is...>{};

namespace aux{
template<class... Ts, unsigned... Is>
std::tuple<Ts&...> tie_all_(std::tuple<Ts...>& other, seq<Is...>){
  return std::tie(std::get<Is>(other)...);
}
} // aux::

template<class... Ts>
std::tuple<Ts&...> tie_all(std::tuple<Ts...>& other){
  return aux::tie_all_(other, gen_seq<sizeof...(Ts)>());
}

コードは次のように使用されますWrapperTuple w(tie_all(t));Member const&これで、コンストラクターを取り除くことができます。

さらに進んで、タプルをラッパー タプルに変換する関数を記述して、型を手動で指定する必要をなくすこともできます。

template<class... Ts>
std::tuple<A<Ts>...> wrap_all(std::tuple<Ts...>& other){
  return tie_all(other);
}
// ...
auto w(wrap_all(t));

また、別のラッパー クラスがある場合:

template<template<class> class Wrapper, class... Ts>
std::tuple<Wrapper<Ts>...> wrap_all_in(std::tuple<Ts...>& other){
  return tie_all(other);
}
// ...
auto w = wrap_all_in<A>(t);
于 2012-11-29T10:56:34.860 に答える
1

全体をラップして の実装を提供するwrapped_tupleテンプレートを作成してみませんか? タプルのレイアウトは固定されており、コンパイラはタプルへの参照が与えられた個々の要素を参照する簡単なコードを発行できるため、個々のタプル要素への参照を保持するのは冗長に思えます。tupleget

例(そしてこれを可変個引数テンプレートなしで行うのは少し面倒です):

template<typename Tuple> class wrapped_tuple;

template<size_t I, typename Tuple>
typename std::tuple_element<I, Tuple>::type&
Get(wrapped_tuple<Tuple>& w) {
  return std::get<I>(w.tuple_);
}

template<typename Tuple> class wrapped_tuple {
  template<size_t I, typename Uple>
  friend typename std::tuple_element<I, Uple>::type&
         ::Get(wrapped_tuple<Uple>& w);
  public:
    wrapped_tuple(Tuple& t) : tuple_(t) {}
  private:
    Tuple& tuple_;
};

template<typename Tuple>
wrapped_tuple<Tuple> wrap_tuple(Tuple& tup) {
  return wrapped_tuple<Tuple>(tup);
}

ここでアイデアオン。

于 2012-11-29T14:34:36.173 に答える