2012-11-14 25 views
2

我希望可以扔一段代碼,我不明白爲什麼它的行爲是這樣的。相同的指針和可變參數類型的問題

我有兩個問題,下面的代碼。

1)爲什麼兩個實例的this指針顯示相同的值?這裏程序的輸出:

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)我嘗試製作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用-O0試過這個。

**編輯**

@ sehe的回答我指出了正確的方向。當然,第二種類型的JV構造函數中不需要void(Is)。僅在第一種類型中,它用於模擬std::fill。但是我們在初始化列表中!序列操作符的默認實現有助於完全消除(void)Is

現在在第二種情況下還有另外一種使用Is,即ptr->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相同?!?

回答

0

由於有兩個打印這個= 0x7ffff1743950出現在「去爲它」之前,他們從構建瓦特。後面的「去尋找它」來自構建copyw,並且我懷疑在這之後你有更多的輸出。

在你JV鍵入的成員:

std::array<T,N> F 

並且由於PS和RS從JV繼承供給到JV,其處理PS是RS,這意味着在F在JV的類型PS派生自具有來自合資企業的PS。

2

這裏的模板參數包的擴展:

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

很有創意,但不是你所期望的東西。

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

是一個單一表達式,使用序列操作者operator,)。其中有大致相同的語義爲「僞功能」的以下塊:

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

Isj怎麼沒有比副作用,他們有沒有任何其他作用。所以整個表達式相當於

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 ++中測試)