如何检查函数中的模板参数是否与给定类型别名的特化相匹配

时间:2018-01-09 22:13:46

标签: c++ templates variadic-templates c++17 type-alias

我有以下代码:

template <template <class...> class Temp, class Specialization>
struct IsSpecialization : std::false_type {};


template <template <typename...> class Temp1,
          template <typename...> class Temp2, typename... Ts>
struct IsSpecialization<Temp1, Temp2<Ts...>>
    : std::is_same<Temp1<Ts...>, Temp2<Ts...>> {};

struct ExprKindMerge {};
struct ExprKindSequence {};

template <class Tag, class... Args>
struct Expr {
    std::tuple<Args...> tup;

    constexpr std::tuple<Args...> toStdTuple() const {
        return this->tup;
    }

    constexpr std::size_t size() const noexcept {
        return std::tuple_size<decltype(tup)>{};
    }
};

template <class...Args>
using MergeExpr = Expr<ExprKindMerge, Args...>;

template <class...Args>
using SequenceExpr = Expr<ExprKindSequence, Args...>;

此函数有时会收到SequenceExpr<Something>作为模板参数:

template <class FullExpr>
auto f(FullExpr expr) {
    ///**************THIS RETURNS FALSE I EXPECT TRUE
    ///Type of full expr is Expr<ExprKindSequence, ...> which is
    /// the expansion of SequenceExpr<...>
    if constexpr (IsSpecialization<SequenceExpr, FullExpr>::value) 
    //...
}

我希望能够检测FullExpr是否是SequenceExpr的特化,但由于某种未知原因而失败。

2 个答案:

答案 0 :(得分:4)

为什么失败很容易。对于SequenceExpr<ExtraArgs...>Temp2推断为ExprTs...推断为ExprKindSequence, ExtraArgs...。然后Temp1<Ts...>Expr<ExprKindSequence, ExprKindSequence, ExtraArgs...>,显然与Temp2<Ts...>不同。

我知道没有完全通用的方法来做到这一点。毕竟,这样一个假设的模板可能需要返回true IsSpecialization<std::remove_reference_t, int> ...

如果我们将其限制为在可推导的上下文中使用其参数的别名模板(在您的示例中就是这种情况),那么一种可能的方法是提出问题&#34;我可以推断Ts... in来自Temp<Ts...>的{​​{1}}?&#34;:

Specialization

答案 1 :(得分:1)

如果您对SequenceExpr的专业化感兴趣 ,我能想象的最好的是添加IsSpecialization的专业化定义如下

template <typename... Ts>
struct IsSpecialization<SequenceExpr, Expr<ExprKindSequence, Ts...>>
    : std::true_type
 { };

显然,这不是我认为不可能的一般解决方案。

如果您使用(例如)f值来呼叫Expr<ExprKindSequence, int, long>

,请记住
f(Expr<ExprKindSequence, int, long>{});  // result true !!!

匹配上方IsSpecialization的专业化,您得到true;我不知道你想要的是什么。

以下是一个完整的工作示例

#include <tuple>
#include <iostream>
#include <type_traits>

template <template <typename...> typename Temp, typename Specialization>
struct IsSpecialization : std::false_type
 { };

template <template <typename...> class Temp1,
          template <typename...> class Temp2, typename... Ts>
struct IsSpecialization<Temp1, Temp2<Ts...>>
    : std::is_same<Temp1<Ts...>, Temp2<Ts...>>
 { };

struct ExprKindMerge {};
struct ExprKindSequence {};

template <typename Tag, typename... Args>
struct Expr
 {
    std::tuple<Args...> tup;

    constexpr std::tuple<Args...> toStdTuple () const
     { return this->tup; }

    constexpr std::size_t size () const noexcept
     { return std::tuple_size<decltype(tup)>{}; }
 };

template <typename ... Args>
using MergeExpr = Expr<ExprKindMerge, Args...>;

template <typename ... Args>
using SequenceExpr = Expr<ExprKindSequence, Args...>;

template <typename ... Ts>
struct IsSpecialization<SequenceExpr, Expr<ExprKindSequence, Ts...>>
    : std::true_type
 { };

template <class FE>
auto f (FE expr)
 { std::cout << IsSpecialization<SequenceExpr, FE>::value << std::endl; }

int main ()
 {
   f(SequenceExpr<int, long>{});             // print 1
   f(Expr<ExprKindSequence, int, long>{});   // print 1  (?)
   f(Expr<int, long>{});                     // print 0
   f(int{});                                 // print 0
 }