0

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?

bradgonesurfing
  • 30,949
  • 17
  • 114
  • 217
  • 2
    TMP existed before `std::enable_if()`. `std::enable_if()` and other convenience functions were added to reduce the amount of boilerplate needed. Your first sentence seems to reveal the flaw in your thinking. The metaprogramming through function overloads **is** SFINAE. If you're tagging C++20, concepts would be the way to go anyway. – sweenish Oct 12 '22 at 16:43
  • [Get this book](https://en.wikipedia.org/wiki/Modern_C%2B%2B_Design). It more than likely discusses how to do `std::enable_if`, since it came out before `std::enable_if` was developed. – PaulMcKenzie Oct 12 '22 at 16:45
  • 2
    You can just combine the two ambiguous overloads and have a single overload `return std::is_same_v;`. None of the `consteval` do anything useful here by the way. There won't be any performance or usage difference compared to `constexpr`. – user17732522 Oct 12 '22 at 16:48
  • @user17732522 Thanks. That's an obvious improvement https://godbolt.org/z/W8MTEhsMs – bradgonesurfing Oct 12 '22 at 16:51
  • 2
    @bradgonesurfing: If you're using C++20, can't you just use `requires` and `concept`s? – Nicol Bolas Oct 12 '22 at 16:53
  • @NicolBolas Yes that's true but I'm just exploring stuff. – bradgonesurfing Oct 12 '22 at 16:53
  • @NicolBolas I think my code is useful for building concepts. It's a strategy for building an expression to put in a requires expression. Is it possible to solve this more succinctly using a requires statement? https://godbolt.org/z/aPehdcY1x – bradgonesurfing Oct 12 '22 at 17:03

1 Answers1

2

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>;
}
Barry
  • 286,269
  • 29
  • 621
  • 977