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