glades
glades

Reputation: 4888

Require certain specialization of template as a template parameter

I have a datastructure meta_array which is instantiated with a variant as type T parameter. The requirement is that the std::variant specialization must contain the type meta_array_head_t as this is is used for tracking information. Can I somehow state this as a requirement using type traits (C++17)?

CompilerExplorer:

#include <cstdio>
#include <variant>
#include <array>

struct meta_array_head_t {
    // end_;
    // remaining_;
    // prev_;
};

template <typename T, size_t S> // make sure T = std::variant<... , meta_array_head_t, ...>
struct meta_array
{
    std::array<T, S> data_; 
};


using val = std::variant<std::monostate, int, double, meta_array_head_t>;

int main()
{
    meta_array<val, 100> marray;
}

Upvotes: 2

Views: 361

Answers (3)

463035818_is_not_an_ai
463035818_is_not_an_ai

Reputation: 123431

You can use specialization to write the trait. Rather than allowing the user to make the mistake and only produce an error you could add the type to the variant when it isnt present:

#include <iostream>
#include <type_traits>
#include <variant>

struct foo {};

template <typename T>
struct has_foo_as_variant : std::false_type {};

template <typename... Ts>
struct has_foo_as_variant<std::variant<Ts...>> {
    static constexpr bool value = (std::is_same_v<Ts,foo> || ...);
};

template <typename T>
constexpr bool has_foo_as_variant_v = has_foo_as_variant<T>::value;

template <typename T>
struct variant_with_foo;

template <typename...Ts>
struct variant_with_foo< std::variant<Ts...>> {
    using type = std::conditional_t< has_foo_as_variant_v<std::variant<Ts...>>,
                                    std::variant<Ts...>,
                                    std::variant<foo,Ts...>>;
};

int main() {
    using good_t = std::variant<foo,int,double>;
    using bad_t = std::variant<int,double>;
    std::cout << has_foo_as_variant_v<good_t>;
    std::cout << has_foo_as_variant_v<bad_t>;
    using foo_added_t = typename variant_with_foo<bad_t>::type;
    std::cout << has_foo_as_variant_v<foo_added_t>;
}

Upvotes: 0

Nimrod
Nimrod

Reputation: 3563

Use std::disjunction_v instead of fold expression for short-circuiting.

Based on @康桓瑋's answer.

template<typename T>
struct meta_variant : std::false_type { };

template<typename... Ts>
struct meta_variant<std::variant<Ts...>> {
      static constexpr bool value = std::disjunction_v<std::is_same<Ts, meta_array_head_t>...>;
};

Demo

Upvotes: 3

康桓瑋
康桓瑋

Reputation: 43106

First, determine that T is of type std::variant, then use fold expression to detect whether the alternative type contains meta_array_head_t.

#include <variant>

template<typename T>
struct meta_variant : std::false_type { };

template<typename... Ts>
struct meta_variant<std::variant<Ts...>> : 
  std::bool_constant<((std::is_same_v<Ts, meta_array_head_t> || ...))> { };

template <typename T, size_t>
struct meta_array {
  static_assert(meta_variant<T>::value, 
    "T must be a std::variant specialization "
    "containing an alternative type of meta_array_head_t.");
};

Demo

Upvotes: 5

Related Questions