2

是否可以使用C++ 11 initializer_list組裝一遞歸定義類如Foo,下面,使用constexpr構造:使用C++ 11 initializer_list用遞歸定義的類型使用constexpr

template <size_t N> 
struct Foo { 
    constexpr Foo(int x, Foo<N-1> f) : x(x), xs(xs) {} 
    int x; 
    Foo<N-1> xs; 
}; 

template <> struct Foo<0> {}; 

我可以初始化一個Foo<3>使用:

int main(int argc, char *argv[]) 
{ 
    Foo<3> a = Foo<3>(1,Foo<2>(2,Foo<1>(3,Foo<0>()))); 
    return 0; 
} 

這將是很好使用的Foo < 3> A = {1,2,3}代替。如果initializer_list中有constexpr tail函數,我認爲它應該起作用。

+0

是的,可以使用該類的'initializer_list'編寫一個構造函數。不相關:如果你專門用'Foo <1>'而不是零,這個班會更小。 – 2012-03-13 00:03:53

+0

爲什麼班級模板會變小? – user2023370 2012-03-13 21:26:37

+0

除特殊情況(其中您的不符合條件),所有類都至少有1個字節。所以'Foo'將總是有一個字節,即'Foo <0>'。 – 2012-03-13 21:40:35

回答

9

是的,以一種四捨五入的方式,有效地將初始化程序列表打包並重新打包爲更適合的格式。然而,有一個更好的(imho)方式:可變模板。

#include <stddef.h> 
#include <iostream> 

template <size_t N> 
struct Foo { 
    template<class... Tail> 
    constexpr Foo(int i, Tail... t) : x(i), xs(t...) {} 

    void print(){ 
    std::cout << "(" << x << ", "; 
    xs.print(); 
    std::cout << ")"; 
    } 

    int x; 
    Foo<N-1> xs; 
}; 

template <> 
struct Foo<1> { 
    constexpr Foo(int i) : x(i) {} 
    void print(){ std::cout << "(" << x << ")"; } 
    int x; 
}; 

int main(){ 
Foo<3> x = {1, 2, 3}; 
x.print(); 
std::cout << "\n"; 
} 

輸出按預期:

(1,(2,(3)))

注意,我選擇1作爲鹼的情況下,因爲它簡單地更有意義。

+2

是的,這絕對是更好的選擇。 +1 – 2012-03-13 00:19:44

+0

啊哈,我以爲你不得不使用'initialization_list'。這看起來不錯。 – user2023370 2012-03-13 10:17:36

+0

@user:統一初始化語法背後的目標之一是能夠實例化任何類,而不管構造函數(或缺少構造函數)。如果存在'initializer_list'構造函數是首選的,但如果不存在,編譯器會試圖將構造函數與值列表進行匹配。這只是提醒我初始化可能寫成'Foo <3> x(1,2,3);'也是。哦,好吧... – Xeo 2012-03-13 16:44:13

1

我沒有編譯器,可以編譯它,但我認爲正確的答案是沿着線的東西:

template <size_t N> 
struct Foo { 
    constexpr Foo(int x, Foo<N-1> f) //template iterator constructor 
    : x(x), xs(xs) {} 
    Foo(std::initializer_list<int> f) //initializer list constructor 
    : x(*f.begin()), xs(++f.begin(), f.end()) 
    { static_assert(xs.size()==N, "incorrect number of values in initializer list");} 
    template<class iter> 
    Foo(iter first, iter last) //template iterator constructor 
    : x(*first), xs(++first, last) {} //UB if wrong number of values given 

    int x; 
    Foo<N-1> xs; 
}; 

template <> 
struct Foo<1> { //I use 1 for smaller structures 
    constexpr Foo(int f) 
    : x(f) {} 
    Foo(std::initializer_list<int> f) 
    : x(*f.begin()) 
    { static_assert(xs.size()==1, "incorrect number of values in initializer list");} 
    template<class iter> 
    Foo(iter first, iter last) 
    : x(*first) 
    { assert(first+1 == last); } 

    int x; 
}; 

對於一個遞歸結構,初始化列表必須傳遞給遞歸地使用迭代器的構造函數。

+0

請注意,這有UB的行爲,因爲我沒有制定出如何使用initializer_lists,但這大致是這個概念。 Xeo說他知道如何「修復」我的代碼,這很好。 – 2012-03-13 16:35:18

0

的解決方案是讓一個函數調用

template<class T> 
constexpr T initlist_val(initializer_list<T>& list, int index) { 
    return (index < list.size()) ? *(list.begin() + index) : 0; 
} 

現在你可以去

class MyClass { 
public: 
    int A, int B; 
    constexpr MyClass(const initializer_list<int>& list) : A(initlist_val(list,0)), B(initlist_val(1)) { 
    // Put nothing here etc.. 
    } 

}; 

你不需要所有其他的東西。這可以與GCC協同工作,而不用其他任何測試。在規則方面可能不正確。

+1

這是怎麼回答這個問題? – 2014-07-01 04:07:16

+0

'在規則方面可能是不正確的.'不要發佈它,那麼!(A)與Q無關,肯定是「不正確」的; (B)不理解統一的初始化,因此通過將'std :: initializer_list'強制轉換爲成員初始化的角色來重新發明輪子,對此,正常的*加強的init-list *已經足夠了; (C)通過不對元素數量進行「斷言」來邀請UB; (D)假設'T'總是可以轉換爲'0'; (E)儘管'initializer_list'是一對小指針,stdlib傳值等等,所以通過引用傳遞。 – 2016-08-20 07:31:43