简体   繁体   中英

How to check if a template parameter in a function matches a specialization of a given type alias

I have the following code:

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

And this function, which sometimes receives a SequenceExpr<Something> as the template parameter:

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) 
    //...
}

I want to be able to detect if FullExpr is a specialization of SequenceExpr but it fails for some unknown reason.

Why it fails is easy. For a SequenceExpr<ExtraArgs...> , Temp2 is deduced to be Expr and Ts... is deduced to be ExprKindSequence, ExtraArgs... . Then Temp1<Ts...> is Expr<ExprKindSequence, ExprKindSequence, ExtraArgs...> and is obviously not the same as Temp2<Ts...> .

I know of no fully generic way to do this. After all, such a hypothetical template would presumably need to return true for IsSpecialization<std::remove_reference_t, int> ...

If we limit it to alias templates that use their parameters in deducible contexts (which is the case in your example), then one possible way is to ask the question "Can I deduce the Ts... in Temp<Ts...> from Specialization ?":

namespace detail {
    template<class> class type {};
    template<template<class...> class Temp, class...Ts>
    void try_deduce(type<Temp<Ts...>>);
}

template <template <class...> class, class, class = void>
struct IsSpecialization : std::false_type {};

template <template <typename...> class Temp, class Specialization>
struct IsSpecialization<Temp, Specialization, 
                        decltype(detail::try_deduce<Temp>(detail::type<Specialization>()))>
    : std::true_type {};

static_assert(IsSpecialization<SequenceExpr, SequenceExpr<int>>()());
static_assert(IsSpecialization<Expr, SequenceExpr<int>>()());
static_assert(!IsSpecialization<MergeExpr, SequenceExpr<int>>()());
static_assert(!IsSpecialization<SequenceExpr, MergeExpr<int>>()());

If your interested only in detecting specializations of SequenceExpr , the best I can imagine is to add a specialization of IsSpecialization defined as follows

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

Obviously this isn't a general solution that I don't think it's possible.

Take in count that if you call f with a (by example) Expr<ExprKindSequence, int, long> value

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

the specialization of IsSpecialization above match and you get true ; I don't know if is what do you want.

The following is a full working example

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM