问:采用普通类型或模板模板参数的模板 class

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

最近我设计了元类型和允许编译时类型连接的可能操作:

#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>>, "");
}

不带模板参数的std::tuple不能作为类型使用,但仍可能出现在模板模板参数中。

现在如果我们更进一步,问题是有没有办法统一struct MetaTypeTagstruct TypeTag,因为它们都是空的class模板参数,或者至少可以使用相同的变量模板 type_tag 但根据类型类别重定向到不同的 class?所以我会想象这样的事情:

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>

我尝试了所有可能的方法——重新定义、显式特化、部分特化、可选模板参数、有条件地使用别名,但 none 奏效了。我曾希望 C++17 的 template<auto> 会有所帮助,但事实证明它仅适用于非类型。

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

我不这么认为。 我能想象的最好的简化一点(非常一点)你的代码是定义几个重载的 constexpr 函数,比如 getTag()

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

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

因此您可以调用 getTag<T>(),其中 T 是类型或模板。

所以你可以这样调用combine()

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

但我不认为这是一个很大的改进。