Can template variables be used as template parameters (similar to class templates)?

C ++ 17 gives us a keyword auto

for non-type template parameters. Is there a way to combine this with a template template parameter that can then be used with the template variables as arguments?

template <template <typename> auto X> // <-- This seems to be illegal
struct Foo
{
};

      

Background:
I want to implement copy_if for the type_vector class. Since all the conditions I want to use are available as template variables, one way to implement it is:

template <typename Container, 
          template <typename> auto Condition> // If this were allowed
struct copy_if;

template <typename... Ts,
          template <typename> auto Condition>
struct copy_if<type_vector<Ts...>, Condition>
{
  using type = decltype(
      (type_vector<>{} + ... + 
          std::conditional_t<Condition<Ts>, 
                             type_vector<Ts>,
                             type_vector<>>{}));
};

      

Of course, I could wrap all my variables into the template structures that matter, but I would rather avoid that.

+3


source to share


1 answer


Perhaps an extra predicate parameter (constexpr lambda, available since C ++ 17) will suffice if a parameter is set to match your variable template?

#include <algorithm>
#include <iostream>
#include <string>
#include <type_traits>

template <class T>
constexpr bool condition = true;

template <>
constexpr bool condition<int> = false;


template <class T>
struct tag { 
   using type = T;
};

template <class... Ts>
struct type_vector {
    template <class... Ts2>
    type_vector<Ts..., Ts2...> operator+(type_vector<Ts2...>);
};

template <class... Ts,
          class Predicate>
auto copy_if(type_vector<Ts...>, Predicate p)
{
  return decltype(
      (type_vector<>{} + ... + 
          std::conditional_t<p(tag<Ts>{}), 
                             type_vector<Ts>,
                             type_vector<>>{})){};
};

int main() {
    auto predicate = [](auto x){ return condition<typename decltype(x)::type>;};
    std::cout << typeid(decltype(copy_if(type_vector<int, float, double>{}, predicate))).name() << std::endl;
}

      

C ++ filter output:

type_vector<float, double>

      

[live demo]




Edit:

There is a situation where using an artificial tag for the send parameter type can be perceived as cumbersome. If you want to avoid this, there is another way. This effectively avoids calling the lambda templates operator()

it just creates to evaluate the type of the result (which should be equivalent to std::true_type

either std::false_type

):

#include <algorithm>
#include <iostream>
#include <string>
#include <type_traits>

template <class T>
constexpr bool condition = true;

template <>
constexpr bool condition<int> = false;

template <class... Ts>
struct type_vector {
    template <class... Ts2>
    type_vector<Ts..., Ts2...> operator+(type_vector<Ts2...>);
};

template <class... Ts,
          class Predicate>
auto copy_if(type_vector<Ts...>, Predicate p)
{
  return decltype(
      (type_vector<>{} + ... + 
          std::conditional_t<decltype(p(std::declval<Ts>())){}, 
                             type_vector<Ts>,
                             type_vector<>>{})){};
};

int main() {
    auto predicate = [](auto x){ return std::integral_constant<bool, condition<decltype(x)>>{};};
    std::cout << typeid(decltype(copy_if(type_vector<int, float, double>{}, predicate))).name() << std::endl;
}

      

[live demo]

+1


source







All Articles