Q: Template class that takes either a normal type or a template template argument
Asked Answered
D

1

7

Recently I designed meta-types and the possible operations that would allow compile-time type concatenations:

#include <tuple>

template<template<typename...> typename T>
struct MetaTypeTag
{};

/*variable template helper*/
template<template<typename...> typename T>
constexpr MetaTypeTag<T> meta_type_tag = {};

template<typename T>
struct TypeTag
{};

/*comparison*/
template<typename T>
constexpr bool operator==(TypeTag<T>, TypeTag<T>) { return true; }
template<typename T, typename U>
constexpr bool operator==(TypeTag<T>, TypeTag<U>) { return false; }

/*variable template helper*/
template<typename T>
constexpr TypeTag<T> type_tag = {};

template<template<typename...> typename T, typename... Ts>
constexpr TypeTag<T<Ts...>> combine(MetaTypeTag<T>, TypeTag<Ts>...)
{
    return {};
}

int main()
{
    constexpr auto combined_tag = combine(meta_type_tag<std::tuple>, type_tag<int>, type_tag<float>);
    static_assert(combined_tag == type_tag<std::tuple<int, float>>, "");
}

The std::tuple without template arguments cannot be used as a type, but may still appear in the template template parameter.

Now if we try to go one step further, the question is whether there is any way to unify struct MetaTypeTag and struct TypeTag, since they are both empty classes with one template parameter, or at least it could be possible to use the same variable template type_tag but redirect to a different class depending on the type category? So I would imagine something like this:

template<???>
constexpr auto type_tag = ????{};

//use with 'incomplete type'
type_tag<std::tuple> //MetaTypeTag<std::tuple>
//use with regular type
type_tag<int> //TypeTag<int>

I tried all possible ways - redefinition, explicit specialization, partial specialization, optional template parameters, conditional using alias, but none worked. I had hoped C++17's template<auto> would help, but it turns out that one is for non-type only.

Diandre answered 21/8, 2017 at 16:51 Comment(2)
When you say "unify" the MetaTypeTag and TypeTag, what exactly do you mean? Could you give an example of what you expect to achieve please?Miche
Edited. I mean that either by using the same template class, or the same expression to obtain different behaviors. At start I thought this can be solved with specialization, but I was wrong.Diandre
S
3

the question is whether there is any way to unify struct MetaTypeTag and struct TypeTag, since they are both empty classes with one template parameter

I don't thinks so. The best I can imagine to simplify a little (very a little) your code is define a couple of overloaded constexpr function, say getTag()

template <typename T>
auto constexpr getTag ()
 { return TypeTag<T>{}; }

template <template <typename ...> typename T>
auto constexpr getTag ()
 { return MetaTypeTag<T>{}; }

so you can call getTag<T>() where T is either a type or a template.

So you can call combine() as follows

constexpr auto combined_tag
   = combine(getTag<std::tuple>(), getTag<int>(), getTag<float>());

But I don't think is a great improvement.

Stepheniestephens answered 21/8, 2017 at 19:37 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.