Search code examples
c++templatesc++17template-meta-programmingconstexpr

Failing to accumulate compile time constants


I am trying to create a variadic int argument vectors and perform + operators on them.

For example:

vec<1,2,3> v1;
vec<4,5>   v2;
auto res = v1+v2;
res.print(); // should print 5 7 3 vec<5,7,3> is the result.

And now I want to create struct vecSum, which will sum up the vectors given to it;

namespace ex {

    template<int... N>
    struct vec
    {
        static const int size = sizeof...(N);
        void print() {
            ((std::cout << N << " "), ...);
        }
    };

    template<int...N, int...M, int ...S>
    auto add(vec<N...>, vec<M...>, std::index_sequence<S...>) {
        constexpr int arr1[sizeof...(S)]{ N... };
        constexpr int arr2[sizeof...(S)]{ M... };
        return vec<(arr1[S] + arr2[S])...>{};
    }

    template<int...N, int...M>
    auto operator+(vec<N...> v1, vec<M...> v2) {
        constexpr size_t size = std::max(sizeof...(N), sizeof...(M));
        return add(v1, v2, std::make_index_sequence<size>{});
    }

    template<typename... Args>
    auto all(Args... args) { return (... + args); }

    template<typename... Ts>
    struct vecSum
    {
        static constexpr auto res = all(Ts);
    };
}

This test works fine:

ex::vec<1, 2, 3> v1;
ex::vec<4, 5> v2;
ex::vec<2> v3;
auto r = ex::all(v1,v2,v3);
r.print();

This prints 7 7 3;

What I want to achieve is:

vecSum<vec<1,3,4>, vec<3>, vec<3,4,5>> z;
z::res.print(); and I expect 7 7 9

Instead I have this error:

error C2275: 'Ts': illegal use of this type as an expression
error C2119: 'res': the type for 'auto' cannot be deduced from an empty initializer

Can someone please hint what's wrong here? I know I am trying to pass types parameter pack Ts as expression, is there a workaround or fix for this?


Solution

  • I see some little problems in your code...


    1. expand Ts

    In vecSum you define res as follows

    static constexpr auto res = all(Ts);
    

    You have to expand Ts as follows

    // ...............................VVVVV
    static constexpr auto res = all(Ts{}...);
    

    1. std::index_sequence contains std::size_t, not int, values

    Your add() is defined as follows

    template<int...N, int...M, int ...S>
    auto add(vec<N...>, vec<M...>, std::index_sequence<S...>) 
    

    But the S... values are std::size_t

    // ........................VVVVVVVVVVV
    template<int...N, int...M, std::size_t ...S>
    auto add(vec<N...>, vec<M...>, std::index_sequence<S...>) 
    

    or template deduction doesn't works.


    1. constexpr functions

    If vecSum has to be constexpr, you have to define all necessary functions (add(), all(), operator+()) as constexpr.


    1. print() const

    The method vec::print() is better const, given that you want print a constexpr vecSum::res value.


    The following is a full compiling corrected example

    #include <utility>
    #include <iostream>
    #include <type_traits>
    
    namespace ex {
    
        template<int... N>
        struct vec
        {
            static const int size = sizeof...(N);
            void print() const {
                ((std::cout << N << " "), ...);
            }
        };
    
        template<int...N, int...M, std::size_t ...S>
        constexpr auto add(vec<N...>, vec<M...>, std::index_sequence<S...>) {
            constexpr int arr1[sizeof...(S)]{ N... };
            constexpr int arr2[sizeof...(S)]{ M... };
            return vec<(arr1[S] + arr2[S])...>{};
        }
    
        template<int...N, int...M>
        constexpr auto operator+(vec<N...> v1, vec<M...> v2) {
            constexpr size_t size = std::max(sizeof...(N), sizeof...(M));
            return add(v1, v2, std::make_index_sequence<size>{});
        }
    
        template<typename... Args>
        constexpr auto all(Args... args) { return (... + args); }
    
        template<typename... Ts>
        struct vecSum
        {
            static constexpr auto res = all(Ts{}...);
        };
    }
    
    int main ()
     { 
    
       ex::vecSum<ex::vec<1,3,4>, ex::vec<3>, ex::vec<3,4,5>> z;
       z.res.print();
     }