Recursive type_traits to define a member function

I am trying to apply type_trait has_funrecursively to Cinclude a member function funonly if it Thas one. Is there a way to make C::funconditionally detected?

template <typename T>
    struct has_fun {
        template <class, class> class checker;
        template <typename U>
        static std::true_type test(checker<U, decltype(&U::fun)> *);
        template <typename U>
        static std::false_type test(...);
        static const bool value = std::is_same<std::true_type, decltype(test<T>(nullptr))>::value;
      };

struct A {
    void fun(){
        std::cout << "this is fun!" << std::endl;
    }
};

struct B {
    void not_fun(){
        std::cout << "this is NOT fun!" << std::endl;
    }
};

template<typename T>
struct C {
    void fun() {
        static_assert(has_fun<T>::value, "Not fun!");
        t.fun();
    }
    T t;
};

int main(int argc, char const *argv[])
{

    std::cout << has_fun<A>::value << std::endl;
    std::cout << has_fun<B>::value << std::endl;
    std::cout << has_fun<C<A>>::value << std::endl;
    std::cout << has_fun<C<B>>::value << std::endl;
}

Conclusion:

1
0
1
1

Expected Result:

1
0
1
0
+4
source share
4 answers

You must enable the SFINAE compiler in the method.

All checks that occur in templates take into account only signatures for functions, so the static_assert used is not considered.

The solution is to add verification to the signature.

Intuitively you write

template<typename T>
struct C {
    std::enable_if_t<has_fun<T>::value> fun() {
        t.fun();
    }

    T t;
};

, : C, C.fun();

?

, , . , C, , foo() , .

, , , .

template<typename T>
struct C {
    template<typename Q=T, typename = if_has_fun<Q>>
    void fun() {
        t.fun();
    }

    T t;
};

, Q T, Q, T, .

https://wandbox.org/permlink/X32bwCqQDb288gVl

: , , .

, , .

template <typename U>
static std::true_type test(checker<U, decltype(std::declval<U>().fun())> *);

. https://wandbox.org/permlink/MpohZzxvZdurMArP

+1
namespace details{
  template<template<class...>class,class,class...>
  struct can_apply:std::false_type{};
  template<template<class...>class Z,class...Ts>
  struct can_apply<Z,std::void_t<Z<Ts...>>,Ts...>:std::true_type{};
}
template<template<class...>class Z,class...Ts>
using can_apply=details::can_apply<Z,void,Ts...>;

template<class T, class...Args>
using dot_fun_r=decltype(std::declval<T>().fun(std::declval<Args>()...));

template<class T, class...Args>
using can_dot_fun = can_apply<dot_fun_r, T, Args...>;

can_dot_fun - slicker has_fun.

temple<class U=T&,
  std::enable_if_t< can_dot_fun<U>{}, bool > =true
void fun() {
    static_cast<U>(t).fun();
}

C<B>{}.fun() , can_dot_fun< C<B>> > .

, (, void_t).

+1

, has_fun

template <typename T>
struct has_fun
 {
   template <typename U>
   static constexpr auto test (int)
      -> decltype( &U::fun, std::true_type{} );

   template <typename U>
   static constexpr std::false_type test (...);

   static constexpr bool value = decltype(test<T>(1))::value;
 };

, , T ( ) fun (&T::fun), , , ( ).

, , , (1) fun() (2), fun() .

, , , (SFINAE enable/disablig fun()) C

template <typename T>
struct C
 {
   template <typename U = T>
   auto fun() -> typename std::enable_if<has_fun<U>::value>::type
    {
      static_assert(has_fun<T>::value, "Not fun!");
      t.fun();
    }

    T t;
 };

,

C<A> ca;

ca.fun();

has_fun<C<A>>

std::cout << has_fun<C<A>>::value << std::endl;

, , fun() C<A> .

: fun() T void,

  t.fun();

C::fun(), .

: has_fun, , std::declval(), T fun() (void(*)(void) )

template <typename T>
struct has_fun
 {
   template <typename U>
   static constexpr auto test (int)
      -> decltype( std::declval<U>().fun(), std::true_type{} );

   template <typename U>
   static constexpr std::false_type test (...);

   static constexpr bool value = decltype(test<T>(1))::value;
 };

Now it’s also has_fun<C<A>>::valuetrue, because it also works in case of overload and template function; Now the method C::fun()is safe because it is enabled only if it Thas a method fun()with the correct signature.

Below is a complete working example

#include <iostream>
#include <type_traits>

template <typename T>
struct has_fun
 {
   template <typename U>
   static constexpr auto test (int)
      -> decltype( std::declval<U>().fun(), std::true_type{} );

   template <typename U>
   static constexpr std::false_type test (...);

   static constexpr bool value = decltype(test<T>(1))::value;
 };

struct A
 { void fun(){ std::cout << "this is fun!" << std::endl; } };

struct B
 { void not_fun(){ std::cout << "this is NOT fun!" << std::endl; } };

template <typename T>
struct C
 {
   template <typename U = T>
   auto fun() -> typename std::enable_if<has_fun<U>::value>::type
    {
      static_assert(has_fun<T>::value, "Not fun!");
      t.fun();
    }

    T t;
 };

int main ()
 {
   std::cout << has_fun<A>::value << std::endl;
   std::cout << has_fun<B>::value << std::endl;
   std::cout << has_fun<C<A>>::value << std::endl;
   std::cout << has_fun<C<B>>::value << std::endl;
 }
+1
source

This can be done with two C implementations, one with fun, the other without, and the optional argument std :: enable_if_t artempt:

template<typename T, std::enable_if_t<has_fun<T>::value> * = nullptr>
struct C
{
 void fun()
 { ... }
};
template<typename T, std::enable_if_t<!has_fun<T>::value> * = nullptr>
struct C
{
  // no fun()
};

If most of C will actually be split between the two cases, you can also unscrew this common part into the database.

0
source

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


All Articles