c++ std::enable_if .... else?
Asked Answered
O

2

8
#include <stdio.h>
#include <type_traits>

void print()
{
    printf("cheers from print !\n");
}

class A 
{
  public:
  void print()
  {
      printf("cheers from A !");
  }
};


template<typename Function>
typename std::enable_if< std::is_function< 
                                typename std::remove_pointer<Function>::type >::value,
                                void >::type 
run(Function f)
{
    f();
}


template<typename T>
typename std::enable_if< !std::is_function< 
                                typename std::remove_pointer<T>::type >::value,
                                void >::type 
run(T& t)
{
    t.print();
}



int main()
{
    run(print);

    A a;
    run(a);

    return 0;
}

The code above compiles and print as expected:

cheers from print ! cheers from A !

what I would like to express is : "if the template is function then apply this function, else ...". Or in another formulation : having a version of the function for function templates, and a default version for non function templates.

so, this part seems somehow redundant, and could be "replaced" by a "else" condition :

template<typename T>
typename std::enable_if< !std::is_function< 
                                typename std::remove_pointer<T>::type >::value,
                                void >::type 
run(T& t)

would this exists ?

Onslaught answered 17/2, 2020 at 20:14 Comment(4)
No, but you can simplify your expression with using statements to reduce verbosity.Lasky
@sturcotte06 no idea what you mean --;Onslaught
As you commented and ask for "outdated" C++11: If you restricted to use older C++ standards, you should flag your question also by the C++11 tag.Chyme
@Chyme the current answers provides answers for both c++17, and previous versions, so I guess it can be useful for any version usedOnslaught
A
10

What you are looking for is constexpr if. That will let you write the code like

template<typename Obj>
void run(Obj o)
{
    if constexpr (std::is_function_v<std::remove_pointer_t<Obj>>)
        o();
    else
        o.print();
}

Live Example

If you don't have access to C++17 but do have C++14, you can at least shorten the code you need to write using a variable template. That would look like

template<typename T>
static constexpr bool is_function_v = std::is_function< typename std::remove_pointer<T>::type >::value;

template<typename Function>
typename std::enable_if< is_function_v<Function>, void>::type 
run(Function f)
{
    f();
}


template<typename T>
typename std::enable_if< !is_function_v<T>, void>::type 
run(T& t)
{
    t.print();
}

Live Example

Actinolite answered 17/2, 2020 at 20:19 Comment(7)
@Onslaught This is a C++17 and up solution. If you need C++11 support, you're left with using a using statement to make an alias.Actinolite
@Actinolite thx ! "you're left with using a using statement to make an alias" : no idea what this means --;Onslaught
@Onslaught Added to the answer. It's actually a variable template that is needed.Actinolite
@Onslaught fwiw variable templates are also not C++11, if you have to use C++11, better use the specific tag. A lot has changed since thenStarofbethlehem
@Actinolite this gives me : "error: ‘is_function_v’ does not name a type"Onslaught
@Onslaught Oops. I forgot the type. Code fixed now.Actinolite
@Actinolite awesome :)Onslaught
D
6

You can use the tag dispatch mechanism if you are limited to using C++11.

namespace detail
{
   template<typename Function>
   void run(std::true_type, Function& f)
   {
      f();
   }

   template<typename Object>
   void run(std::false_type, Object& o)
   {
      o.print();
   }

} // namespace detail

template<typename T>
void run(T& t)
{
   constexpr bool t_is_a_function = 
      std::is_function<typename std::remove_pointer<T>::type >::value;
   using tag = std::integral_constant<bool, t_is_a_function>;
   detail::run(tag{}, t);
}

Working example.

Doriadorian answered 17/2, 2020 at 20:46 Comment(1)
It should be emphasized that while tagged dispatch is a bit more verbose - e.g. has more functions defined - it is more readable, especially to people who aren't familiar with template metaprogramming. It is also easier to debug; SFINAE is notorious for subtle failures.Methylnaphthalene

© 2022 - 2024 — McMap. All rights reserved.