2

なぜこのように動作するのか理解できないコードを挿入しても問題ないことを願っています。

次のコードには 2 つの問題があります。

1) this2 つのインスタンスのポインターが同じ値を示しているのはなぜですか? プログラムの出力は次のとおりです。

WJ::WJ(Jit&)
this = 0x7ffff1743950 JV<T, N>::JV(Jit&, indices<Is ...>) [with int ...Is = {0}; T = WJ<float>; int N = 1]
RS<T>::RS(Jit&) [with T = WJ<float>]
this = 0x7ffff1743950 JV<T, N>::JV(Jit&, indices<Is ...>) [with int ...Is = {0}; T = RS<WJ<float> >; int N = 1]
PS<T>::PS(Jit&) [with T = RS<WJ<float> >]
go for it
ptr = 0x7ffff1743950 JV<T, N>::JV(Jit&, JV<T, N>*, indices<Is ...>) [with int ...Is = {0}; T = RS<WJ<float> >; int N = 1]
PS<T>::PS(const PS<T>&) [with T = RS<WJ<float> >; PS<T> = PS<RS<WJ<float> > >]

2 倍の値を示します0x7ffff1743950。2番目のインスタンスが作成される前に最初のインスタンスが破棄されていないと確信しているので、これは私を驚かせます。

2)が元に設定されているPS場所のディープ コピーを作成しようとします。orig_ptrここで使用されるセットアップは、再帰的なテンプレートのインスタンス化セットアップです。したがってorig_ptr、各レベルで階層を尊重し、それに応じてポイントする必要があります。私が理解していないのは、コードがコンパイルされる理由F{{(void(Is),j,ptr->F[Is])...}}(コードでマークされている) と、コンパイルされない理由F{{(void(Is),j,&ptr->F[Is])...}}です。(これは正しいと思います)。コンパイラーが呼び出しているT(別名) のコンストラクターがわかりません。ポインタ版しかRS<WJ<float> >存在しません。RS<WJ<float> >::RS(Jit&,RS<WJ<float> >)

#include<iostream>
#include<array>

struct Jit {};


template <int... Is>
struct indices {};

template <int N, int... Is>
struct build_indices
  : build_indices<N-1, N-1, Is...> {};

template <int... Is>
struct build_indices<0, Is...> : indices<Is...> {};


template<class T,int N>
struct JV {

  JV(Jit& j) : JV(j,build_indices<N>{}) {}
  template<int... Is>
  JV(Jit& j, indices<Is...>) : 
    jit(j), F{{(void(Is),j)...}} {
    std::cout << "this = " << (void*)this << " " << __PRETTY_FUNCTION__ << "\n";
  }


  JV(Jit& j,JV<T,N>* ptr) : JV(j,ptr,build_indices<N>{}) {}
  template<int... Is>
  JV(Jit& j,JV<T,N>* ptr, indices<Is...>) : 
    // Why does this not compile with &ptr->F[Is] ??
    // What is it calling now (there is no T::T(Jit&,sub_T))
    jit(j), orig_ptr(ptr), F{{(void(Is),j,ptr->F[Is])...}} {  
    std::cout << "ptr = " << (void*)ptr << " " << __PRETTY_FUNCTION__ << "\n";
  }
  std::array<T,N> F;
  JV<T,N>* orig_ptr;
  Jit& jit;
};

template<class T>
struct RS : public JV<T,1>
{
  RS(Jit &j): JV<T,1>(j) {
    std::cout << __PRETTY_FUNCTION__ << "\n";
  }

  RS(Jit &j, RS* orig): JV<T,1>(j,orig) {
    std::cout << "orig = " << orig << " " << __PRETTY_FUNCTION__ << "\n";
  }
};

template<class T>
struct PS : public JV<T,1>
{
  PS(Jit& j): JV<T,1>(j) {
    std::cout << __PRETTY_FUNCTION__ << "\n";
  }

  PS(const PS& rhs) : JV<T,1>(rhs.jit,const_cast<JV<T,1>*>(  static_cast<const JV<T,1>*>(&rhs) ) ) {
    std::cout << __PRETTY_FUNCTION__ << "\n";
  }
};

template<class T>
struct WJ
{
  WJ(Jit& j) {
    std::cout << "WJ::WJ(Jit&)\n";
  }
  WJ(Jit& j,WJ* ptr) {
    std::cout << __PRETTY_FUNCTION__ << "\n";
  }
};

int main() {
  Jit j;
  PS<RS<WJ<float> > > w(j);
  std::cout << "go for it\n";
  PS<RS<WJ<float> > > wcopy(w);
}

** 編集 **

WJのインスタンス化手順がF全体を再帰できるように、ポインター インターフェイスを追加しました。SFINEのせいかと思いました。しかし、そうではありませんでした。

g++-4.7 (Ubuntu/Linaro 4.7.2-4precise1) 4.7.2 with でこれを試しました-O0

** 編集 **

@seheの答えは私を正しい方向に向けました。もちろん、void(Is)2 番目のタイプのJVコンストラクターでは必要ありません。最初のタイプでのみ、 をシミュレートするために使用されstd::fillます。しかし、イニシャライザリストにはあります! シーケンス演算子のデフォルトの実装は、完全に排除するのに役立ちます(void)Is

2 番目のケースでは、 の別の使用法Is、つまり inptr->F[Is]があるため、人工的な を導入する必要はありませんvoid。今、これはより良く見えます:

** 編集 **

JV(Jit& j,JV<T,N>* ptr, indices<Is...>) : 
    jit(j), orig_ptr(ptr), F{{T(j,&ptr->F[Is])...}} { }

コンパイルして正常に動作するようになりました!

ただし、1 つの問題がまだ残っています: なぜthis2x が同じなのか?!?

4

2 に答える 2

2

テンプレート引数パックの展開は次のとおりです。

F {{(void(Is),j,ptr->F[Is])...}}

非常に創造的ですが、あなたが期待するものではありません。

(void(Is),j,ptr->F[Is])

は、シーケンス演算子( )を使用した単一の式operator,です。これは、次の「疑似関数」のブロックとほぼ同じセマンティクスを持っています。

{
    (void)Is;
    (void)j;
    return ptr->F[Is];
}

副作用以外の効果はありません。副作用はありませIsん。jしたがって、式全体は次と同等です

F {{(ptr->F[Is])...}}

正直なところ、私はあなたのコードの意図を把握できませんでした。これは、あなたが求めていると思われる構文が機能するかどうかを検証するために私が行った概念の小さな証明です

#include <iostream>
#include <vector>
#include <array>

typedef std::vector<std::string> Vec;

template <int... I>
    void foo(Vec const& v)
{
    std::array<std::string, sizeof...(I)> expand {{ v.at(I)... }};
    for(auto i: expand)
        std::cout << i << '\n';
}

int main()
{
    const Vec v { "zero", "one", "two", "three", "four", "etc" };
    foo<2,1,3,0>(v);
    foo<42>(v);
}

出力:

two
one
three
zero
terminate called after throwing an instance of 'std::out_of_range'
  what():  vector::_M_range_check

したがって、まさに期待通りの動作をします (GCC および Clang++ でテスト済み)。

于 2012-11-14T20:59:33.670 に答える
0

this = 0x7ffff1743950 が「go for it」の前に表示される 2 つのプリントがあるため、それらは w の構築によるものです。「go for it」に続くものは copyw の構築から来ており、その後に this = で追加の出力があると思われます。

あなたのJVタイプでは、あなたはメンバーです:

std::array<T,N> F

また、PS と RS の両方が JV から JV に提供された型を継承するため、PS を処理するのは RS です。つまり、PS の派生元である JV 内の F には、JV から派生した PS があります。

于 2012-11-14T17:36:29.300 に答える