Search code examples
c++templatesstlconstexpr

Constructor for a struct with exactly N arguments, where N is a template parameter


I want to create a very small math library, which has good support for constexpr.

To simplify the problem, let's just talk about vectors here:

template <typename T, unsigned int N> struct vec {
  std::array<T, N> m_data;
};

typedef vec<float, 2> vec2;

Now, I want to create a constructor for the vec struct, which should look like this:

constexpr vec2 x = vec2(1.0f, 2.0f);
// or even nicer but probably not possible
constexpr vec2 y = {1.0f, 2.0f};

I would like the second example more, because it would allow for better construction of matricies as well, but I don't believe that's possible in C++17 without losing the const evaluation, because it might require initializer_lists, which are not consteval, as far as I know, because they have a dynamic range.

But even with the first example, I'm not sure how to implement this.

I found this thread, which proposes:

template <std::size_t N>
struct foobar
{
    template <typename ...Args, typename = typename std::enable_if<N == sizeof...(Args), void>::type>
    foobar(Args&&... args) { ... }
};

Which looks promising (a similar syntax is used in the array.h header):

template<typename _Tp, typename... _Up>
  array(_Tp, _Up...)
    -> array<enable_if_t<(is_same_v<_Tp, _Up> && ...), _Tp>,
         1 + sizeof...(_Up)>;

The problem here is that both are not declared constexpr.

Does anybody have any ideas how to implement this as a constexpr constructor? Additionally, the body is missing in both cases, and I still haven't figured out how to create an array from a parameter pack.

Please don't complain about T not being constrained by a concept. I'm working on it, and as a clarification it doesn't have to use std::array if there are any other options here. I'm very open to any suggestion.


Solution

  • Found a solution

    template <typename T, size_t N> struct vec {
    
      template <typename... E,
                typename = typename std::enable_if_t<
                    std::conjunction_v<std::is_convertible<E, T>...> &&
                        N == (1 + sizeof...(E)),
                    void>>
      constexpr explicit vec(
          T v, E &&...elements) noexcept
          : m_elements({{v, static_cast<float>(std::forward<E>(elements))...}}) {}
    }
    

    Seems to work fine