Is it possible to create and initialize an array of values using template metaprogramming?

前端 未结 7 748
悲&欢浪女
悲&欢浪女 2020-12-04 22:20

I want to be able to create an array of calculated values (let\'s say for simplicity\'s sake that I want each value to be the square of it\'s index) at compile time using t

7条回答
  •  旧巷少年郎
    2020-12-04 23:15

    Although you can't initialise an array in-place like that, you can do almost the same thing by creating a recursive struct:

    template 
    struct squared {
        squared rest;
        int x;
        squared() : x((I - 1) * (I - 1)) {}
    };
    
    template <>
    struct squared<1> {
        int x;
        squared() : x(0) {}
    };
    

    Then later in your code you can declare:

    squared<5> s;
    

    and the compiler will indeed create a struct containing 5 ints: 0, 1, 4, 9, 16.

    A couple of notes:

    1. My interpretation of the C++ standard is that it stops short of guaranteeing that this struct will be laid out identically to an array. While it is a POD type, and POD types are guaranteed to be laid out "contiguously" in memory (1.8/5) with the first member at offset 0 (9.2/17) and later members at higher addresses (9.2/12), and arrays are also laid out "contiguously" (8.3.4/1), the standard doesn't say that arrays are layout-compatible with such structs. However, any sane compiler will lay these objects out identically. [EDIT: As ildjarn points out, the presence of a user-defined constructor actually makes this class non-aggregate and therefore non-POD. Again, any sane compiler will not allow this to affect its layout.]
    2. C++ requires that even an empty struct be at least 1 byte long. If it did not, we could go with a slightly cleaner formulation in which the base case of the recursion was I == 0 and we didn't subtract 1 from I for the calculations.

    It would be nice if we could place this struct inside a union with an array of the appropriate size, to make it easy to access the members. Unfortunately, C++ bans you from including an object in a union if that object has a non-trivial constructor. So the easiest way to get at the ith element is with a good old-fashioned cast:

    squared<5> s;
    cout << "3 squared is " << reinterpret_cast(&s)[3] << endl;
    

    If you wanted, you could write an overloaded operator[]() function template to make this prettier.

提交回复
热议问题