なぜこのように動作するのか理解できないコードを挿入しても問題ないことを願っています。
次のコードには 2 つの問題があります。
1) this
2 つのインスタンスのポインターが同じ値を示しているのはなぜですか? プログラムの出力は次のとおりです。
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 つの問題がまだ残っています: なぜthis
2x が同じなのか?!?