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?

+3


source to share


4 answers


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.

+4


source


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;
};

      

+4


source


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
};

      

+2


source


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;
};

      

0


source







All Articles