Can I determine which label forwarding overload is used at compile time?

Let's say I have a functor that uses label dispatching to select from multiple function implementations, something like this:

// base class for all tags, indicating the "default" implementation
struct tag_base { };

// subclasses for tags that might select a different implementation
struct tag1 : tag_base { };
struct tag2 : tag1  { };
struct tag3 : tag2 { };

struct func
{
    void operator()(tag_base) { }
    void operator()(tag3) { }
};

Thus, in this simple example, tags tag1and tag2would send a call to the operator funcwho takes tag_basethe default implementation. However tag3, it is sent to another implementation instead. I am interested in checking at compile time, for a given type of function funcand two types of tags Tand Uwhether they will send the same call operator overload func.

Basically, I need a trait like this (only pseudocode, since this approach does not compile):

template <typename Func, typename T, typename U>
struct has_same_tag_overload
{
    enum { value =  (void (Func::*)(T)) &func::operator() ==
                    (void (Func::*)(U)) &func::operator() };
}

, :

  • tag<func, tag_base, tag1>::value == 1
  • tag<func, tag_base, tag2>::value == 1
  • tag<func, tag1, tag2>::value == 1
  • tag<func, tag1, tag3>::value == 0
  • tag<func, tag2, tag3>::value == 0

? , ++ 03?

+4
2

, ,

struct tag_base { using base = void; };
struct tag1 : tag_base { using base = tag_base; };
struct tag2 : tag1  { using base = tag1; };
struct tag3 : tag1  { using base = tag1; };
...
// or something like tag4: is_a<tag2> {}; if you don't like typing ...

template<class U>
struct tag_matcher
{
  template<class V, class W = std::enable_if_t<std::is_same<U,V>::value> >
  operator V();
};

template<typename F, typename T>
std::true_type match_tag_impl( decltype(F{}(tag_matcher<T>()))* );

template<typename F, typename T>
std::false_type match_tag_impl( ... );

template<typename F, typename T>
struct match_tag
{
  using type = std::conditional_t< decltype(match_tag_impl<F,T>(0))::value,
    T, typename match_tag<F, typename T::base>::type >;
};

template<typename F>
struct match_tag<F,void> { using type = void; };

template<typename F, typename T, typename U>
struct has_same_tag_overload:
  std::is_same< typename match_tag<F,T>::type, typename match_tag<F,U>::type > {};

, , , , . ++ 11, ++ 03, .

update re ++ 03:

, tag_matcher, , ++ 03, , sfinae . , , :

template<class U>
struct tag_matcher{};

// macro just for expository purposes
#define MATCHME(TAG) template<class T> TAG( tag_matcher<T>, std::enable_if_t<std::is_same<T,TAG>::value>* = 0 )

struct tag_base { using base = void; MATCHME(tag_base); };
struct tag1 : tag_base { using base = tag_base; MATCHME(tag1); };
// ...
+3

, , func. ++ 14 :

#include <type_traits>

template <typename Func, typename Tag1, typename Tag2>
using has_same_tag_overload =
    std::is_same<decltype(Func::fn(Tag1{})), decltype(Func::fn(Tag2{}))>;

struct func
{
    static auto fn(tag_base) {
        return [] { std::cout << "tag_base\n"; };
    }
    static auto fn(tag3) {
        return [] { std::cout << "tag3\n"; };
    }

    template <typename Tag>
    void operator()(Tag tag)
    {
        fn(tag)();
    }
};

, func, , . , std::is_same.

++ 03, . func . decltype sizeof ( func , .

+1

Source: https://habr.com/ru/post/1688619/


All Articles