Variationally templating std :: conditional if one type is a creation error
I am trying to create a variadic templated class. As usual, each level of the instance must instantiate the "next level" by cutting off one type and then using the remainder. For my last level, and not for specializing on one type, I'd rather give some base case type and not duplicate the actual logic.
I added std::conditional
to be included BaseCase
when other types consist 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 lies in the fact that VariadicClass
shabloniziruetsya at least one type of parameter, however, when it enters the base register ( Ts
empty), when trying to use std::conditional
is used VariadicClass<>
, which of course does not pass.
The solution I decltype
worked with was to write some specific functions and use along with overloads and not use at all std::conditional
.
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 continue this practice every time I have a variation class, it seems tedious. Is there a way to use std::conditional
or something similar to achieve this effect without writing out so much problematic code?
source to share
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 Ts...
and returns its argument. delay
takes meaning template
and applies Ts...
. Although the signature looks suspicious, it works.
source to share
Why not easy
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;
};
source to share
After reading TC's answer and Yakk's comment, I realized that I could write this as one templated class with two specializations, rather than writing another one BaseClass
and a type alias.
template <typename... Ts>
class VariadicClass;
// specialization gets everything but an empty Ts
template <typename T, typename... Ts>
class VariadicClass<T, Ts...> {
VariadicClass<Ts...> next_level;
// normal case
};
template <>
class VariadicClass<> { // instead of class BaseCase
// base case
};
source to share
Alternatively, you can specialize VariadicClass<T>
class BaseCase {};
// general case
template <typename T, typename... Ts>
class VariadicClass {
T this_level; // whatever
VariadicClass<Ts...> next_level;
};
// specialization
template <typename T>
class VariadicClass<T> {
T this_level; // whatever
BaseClass next_level;
};
source to share