简体   繁体   中英

Check with which template parameter a class was instantiated (compile time)

I try to write a metafunction type_par_same_as that selects true_type whenever the template parameter(s) of a class match the given types:

Demo

#include <type_traits>
#include <concepts>
#include <string>
#include <vector>
#include <cstdio>

template <template <typename...> class Template, typename T>
struct type_par_same_as_impl : std::false_type {};

template <template <typename...> class Template, typename... Args>
struct type_par_same_as_impl<Template<Args...>, Args...> : std::true_type {};

template <template <typename...> class Template, typename... Args>
concept type_par_same_as = type_par_same_as_impl<Template, Args...>::value;

int main()
{

    std::vector<int> vint;
    std::vector<std::string> vstring;
    if constexpr (type_par_same_as<decltype(vint), int>) {
        printf("Vector instantiated with type int!\n");
    }
}

Here's what I'm getting:

<source>:11:56: error: type/value mismatch at argument 1 in template parameter list for 'template<template<class ...> class Template, class T> struct type_par_same_as_impl'
   11 | struct type_par_same_as_impl<Template<Args...>, Args...> : std::true_type {};
      |                                                        ^
<source>:11:56: note:   expected a class template, got 'Template<Args ...>'

My approach was that a raw template template parameter just takes in any specialization of a template, say eg std::vector (without type). Then I SFINAE-out class types that don't match the template specialization Template<Args...> (eg std::vector<int> in case int was given as Args ) and I should receive true_type for all class types where this can be done. But my logic seems to be off at some point. Where did I go wrong?

Here's how to make it compile:

template <class Template, typename... T>
struct type_par_same_as_impl : std::false_type {};

template <template <typename...> class Template, typename... Args>
struct type_par_same_as_impl<Template<Args...>, Args...> : std::true_type {};

template <class Template, typename... Args>
concept type_par_same_as = type_par_same_as_impl<Template, Args...>::value;

But it won't work with your test case as std::vector<int> is really std::vector<int, std::allocator<int>> and you're passing only the int of the two. So you need:

int main()
{

    std::vector<int> vint;
    std::vector<std::string> vstring;
    if constexpr (type_par_same_as<decltype(vint), int, std::allocator<int>>) {
        printf("Vector instantiated with type int!\n");
    }
}

This can work as originally intended, if, expanding on lorro's answer , Args... are placed in a non-deduced context so they're only deduced from the explicitly passed template parameters, and not the parameters which which std::vector is instantiated, by making use of std::type_identity :

#include <type_traits>
#include <vector>

template <typename Template, typename... Args>
struct type_par_same_as_impl : std::false_type {};

template <template <typename...> typename Template, typename... Args>
struct type_par_same_as_impl<Template<std::type_identity_t<Args>...>, Args...>
    : std::true_type {};

template <typename Template, typename... Args>
concept type_par_same_as = type_par_same_as_impl<Template, Args...>::value;

static_assert(type_par_same_as<std::vector<int>, int, std::allocator<int>>);
static_assert(type_par_same_as<std::vector<int>, int>);
static_assert(not type_par_same_as<std::vector<int>, float>);

Try it on Compiler Explorer

I just wanted to add a solution that I found just now which is possibly a bit more versatile.

Instead of comparing the first template parameter one might as well extract the nth template parameter and use the idiomatic std::is_same<U,T> to compare it. This way the user has the freedom to choose which template parameter is actually compared:

Demo

#include <type_traits>
#include <concepts>
#include <string>
#include <vector>
#include <tuple>
#include <cstdio>

template<std::size_t, typename>
struct nth_targ_of;

template<std::size_t N, template <typename...> class Template, typename... Args>
struct nth_targ_of<N, Template<Args...>> : std::tuple_element<N, std::tuple<Args...>> {};

template<std::size_t N, typename T>
using nth_targ_of_t = nth_targ_of<N, T>::type;

int main()
{
    std::vector<int> vint;
    std::vector<std::string> vstring;
    if constexpr(std::same_as<nth_targ_of_t<0, decltype(vint)>, int>) {
        printf("Vector instantiated with type int!\n");
    }
    if constexpr(std::same_as<nth_targ_of_t<0, decltype(vstring)>, std::string>) {
        printf("Vector instantiated with type string!\n");
    }
}

Output:

Vector instantiated with type int!
Vector instantiated with type string!

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