bradgonesurfing
bradgonesurfing

Reputation: 32192

How to implement metaprogramming in c++ without std::enable_if

I was reading somewhere that it would be possible to do c++ metaprogramming using function overloads instead of SFINAE. I came up with a toy exercise to test this. I want to detect if a particular type is a nested vector. My code that works is

#include <type_traits>
#include <vector>
#include <iostream>

template <typename T> struct Tag {};

template <typename TLeaf >
consteval bool is_nested(Tag<TLeaf>)
{
    return true;
}

template <typename TLeaf , typename TSub, typename enable = std::enable_if_t< !std::is_same_v<TLeaf, TSub> >>
consteval bool is_nested(Tag<TSub>)
{
    return false;
}

template <typename TLeaf , typename TSub>
consteval bool is_nested(Tag<std::vector<TSub>>)
{
    return is_nested<TLeaf>(Tag<TSub>{});
}

template <typename TSub, typename TLeaf>
consteval bool is_nested()
{
    return is_nested<TLeaf>(Tag<TSub>{});
}


int main(){
    using type_nested1 = std::vector<std::string>;
    using type_nested2 = std::vector<type_nested1>;

    std::cout << is_nested<type_nested1, std::string>() << std::endl;
    std::cout << is_nested<type_nested2, std::string>() << std::endl;
    std::cout << is_nested<int, std::string>() << std::endl;
    std::cout << is_nested<type_nested1, int>() << std::endl;
    std::cout << is_nested<type_nested2, int>() << std::endl;
}


https://godbolt.org/z/zGEf9cej5

and the output is

Program returned: 0
Program stdout
1
1
0
0
0

but I'm disappointed that I had to use std::enable_if_t to disambiguate the two overloads. Can this be rewritten to keep the spirit of the exercise but eliminate any SFINAE crud from the solution?

Upvotes: 0

Views: 100

Answers (1)

Barry
Barry

Reputation: 303387

If you just pass two arguments all the way through, you don't need to do anything special:

template <typename T> struct Tag {};
template <typename T> inline constexpr Tag<T> tag;

template <typename T>
consteval bool is_nested(Tag<T>, Tag<T>) {
    return true;
}

template <typename L, typename T>
consteval bool is_nested(Tag<L>, Tag<T>) {
    return false;
}

template <typename L, typename T>
consteval bool is_nested(Tag<std::vector<L>>, Tag<T>) {
    return is_nested(tag<L>, tag<T>);
}

template <typename L, typename T>
consteval bool is_nested() {
    return is_nested(tag<L>, tag<T>);
}

Where you can combine the first two overloads into one:

template <typename L, typename T>
consteval bool is_nested(Tag<L>, Tag<T>) {
    return std::is_same_v<L, T>;
}

Which was also true of your original example (but it's easier to see if all your parameters are in the same spot):

template <typename TLeaf , typename TSub>
consteval bool is_nested(Tag<TSub>) {
    return std::is_same_v<TLeaf, TSub>;
}

Upvotes: 2

Related Questions