I am attempting to build a variadically templated class. As is common, each level of the instantiation needs to instantiate the "next level" by slicing off one type and then using the remainder. For my final level, rather than specialize on one type, I'd rather give some base case type and keep from duplicating the actual logic.
I've added a std::conditional
to switch on the BaseCase
when the rest of the types consists of an empty parameter pack.
class BaseCase { };
template <typename T, typename... Ts>
class VariadicClass;
template <typename... Ts>
using NextLevel = typename std::conditional<
sizeof...(Ts) != 0, VariadicClass<Ts...>, BaseCase>::type;
template <typename T, typename... Ts>
class VariadicClass {
T this_level; // whatever
NextLevel<Ts...> next_level; // fails when Ts is empty
};
The problem is that VariadicClass
is templated on at least one type parameter, so when it hits the base case (Ts
is empty), trying to use std::conditional
uses VariadicClass<>
, which fails of course.
The solution I've managed is to write some specific functions and use decltype
along with overloads, and not use std::conditional
at all.
template <typename... Ts>
VariadicClass<Ts...> type_helper(Ts&&...);
BaseCase type_helper();
template <typename... Ts>
using NextLevel = decltype(type_helper(std::declval<Ts>()...));
Now, this works, but if I want to keep up this practice every time I have a variadic class, it seems tedious. Is there a way to use std::conditional
or something similar to achieve this effect without having to write out so much problem-specific code?
Defer evaluation.
template<class T>struct identity{
template<class...>using result=T;
};
template<template<class...>class src>
struct delay{
template<class...Ts>using result=src<Ts...>;
};
template <typename... Ts>
using NextLevel =
typename std::conditional<
sizeof...(Ts) != 0, delay<VariadicClass>, identity<BaseCase>
>::type::template result<Ts...>;
identity
ignores the Ts...
and returns its argument. delay
takes a template
and applies the Ts...
. While the signature looks suspicious, it works.
Why not just
class BaseCase { };
template <typename... Ts>
class VariadicClass; // undefined base template
template <typename... Ts>
using NextLevel = typename std::conditional<
sizeof...(Ts) != 0, VariadicClass<Ts...>, BaseCase>::type;
template <typename T, typename... Ts>
class VariadicClass<T, Ts...> { // partial specialization for having at least 1 type parameter
T this_level; // whatever
NextLevel<Ts...> next_level;
};
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With