Using array as a non type template parameter

100 views Asked by At

I have a code in which I encode values of various types on the type level with an use of a helper data structure with core interface identical to std::integral_constant, but more generalized and with additional functionalities for specific specializations.

//very condensed demonstration
template <typename T, T v>
struct helpful_constant
{
  static constexpr T value = v;
};

template <auto v>
constexpr helpful_constant<decltype(v), v> constant;

It let's me perform some rather fun looking operations, like such

auto foo = constant<[](const auto& x){return 2*x;}>;
std::cout << foo.value(constant<3.14>.value) << std::endl;

Making it work for array types, including string, requires a bit of indirection. I use an additional helper class for that

template <typename VT, std::size_t s>
struct array_literal
{
  using stored_type = VT;
  constexpr array_literal(const VT (&array)[s]) {
    std::copy(array, array + s, data);
  }
  static constexpr std::size_t size() { return s; }
  VT data[s];
};
template <array_literal array>
constexpr helpful_constant<decltype(array), array> arr;

and with that, I can write something like this

std::cout << arr<"Example">.value.data << std::endl;

It's also somewhat in realms of possibility to write arr<{{3, 6, 8}}>, but this notation is in a somewhat questionable place within standard, more on that here.

However, I somewhat dislike that array_literal requires a non static non const member to work and tried to push the idea further, creating another struct for storing the array as its non type template parameter and then specializing the helpful_constant for the use of it.

struct array_tag{};

template <typename T, typename Tag>
concept having_tag = std::same_as<typename T::tag, Tag>;

template <typename VT, std::size_t s, const VT va[s]>
struct array_object
{
  using tag = array_tag;
  using unit_type = VT;
  static constexpr std::size_t size(){ return s; }
  static constexpr auto data(){ return va; }
};

template <typename VT, std::size_t s, const VT va[s]>
constexpr array_object<VT, s, va> array;

template <having_tag<array_tag> AO, AO ao>
struct helpful_constant<AO, ao>
{
  static constexpr auto value = ao.data();
};

Needed some additional helpers to eliminate warnings about helpful_constant not being specialized enough. Also, getting it to work with a variable template proved itself to be a bit more challenging, but doable.

template <array_literal al>
constexpr auto literal_to_object(){
  return array_object<
    typename decltype(al)::stored_type,
    al.size(),
    //al.data
    helpful_constant<decltype(al), al>::value.data
  >{};
}

template <array_literal al>
constexpr helpful_constant<
  std::invoke_result_t<decltype(literal_to_object<al>)>,
  literal_to_object<al>()
> arr2;
/*...*/
std::cout << arr2<"Example2">.value << std::endl;

The thing that perplexes me here is in the helper function literal_to_object when instantiating array_object, al.data is accepted by the msvc compiler, but gcc gives an error

the address of 'array_literal<char, 9>{"Example2"}' is not a valid template argument

however, it accepts it when I wrap the passed array_literal in the helpful_constant(or std::integral_constant or similar).

Why is that?

godbolt link

I'll just note here that neither of the solutions work for clang, but from what I've observed it generally has some problems with c++20s expanded support for NTTP as of now.

0

There are 0 answers