Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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 e.g. std::vector (without type). Then I SFINAE-out class types that don't match the template specialization Template<Args...> (e.g. 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?

like image 515
glades Avatar asked Sep 20 '25 12:09

glades


2 Answers

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");
    }
}
like image 122
lorro Avatar answered Sep 23 '25 01:09

lorro


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

like image 29
Patrick Roberts Avatar answered Sep 23 '25 03:09

Patrick Roberts