Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Branch on a template parameter in function?

I have a templated function, and at one point I would like to have different code depending on the template parameter:

template <typename T>
void function(const T &param) {
    // generic code here...

    // pseudo-code:
    if constexpr isinstance(param, Banana) {
        param.peel();
    } else if constexpr isinstance(param, Apple) {
        // do nothing, Apple has no method `peel`
    }
}

I don't want to specialize the whole function, since most of the code is shared. The statement I want to insert is acutally a temporary debugging measure. I know the correct thing would be to create a overloaded function doPeel and call that instead:

void doPeel(const Banana &param) { param.peel(); }
void doPeel(const Apple &param) {}

But I'm curious, is there a way to tell at compile time, in a function, what (template specialization) type a given variable is... in order to use statements that only compile for one type?

I wonder if something like that is possible with constexpr - or does the compiler enforce types in a discarded branch? I also tried making up something with lambdas - defining lambdas for both cases and only calling one, but I could not find a way to do it. Any ideas?

like image 583
jdm Avatar asked Oct 24 '16 09:10

jdm


Video Answer


2 Answers

There is if constexpr in C++17:

template<typename T>
void foo(T const& t)
{
    if constexpr(is_same<decay_t<T>, int>::value) {
        cout << __PRETTY_FUNCTION__ << " " << t * 2 << endl;
    } else {
        cout << __PRETTY_FUNCTION__ << endl;
    }
}

live demo


In C++14 you could hack something like this:

template<typename T>
void foo(T const& t)
{
    conditional_eval<is_same<decay_t<T>, int>>([=](auto){
        cout << __PRETTY_FUNCTION__ << " " << t * 2 << endl;
    },[](auto){
        cout << __PRETTY_FUNCTION__ << endl;
    });
}

With conditional_eval defined as:

template<typename IfTrue, typename IfFalse>
void conditional_eval_impl(std::true_type, IfTrue&& t, IfFalse&&) {
    t(0);
}

template<typename IfTrue, typename IfFalse>
void conditional_eval_impl(std::false_type, IfTrue&&, IfFalse&& f) {
    f(0);
}

template<typename Tag, typename IfTrue, typename IfFalse>
void conditional_eval(IfTrue&& t, IfFalse&& f) {
    conditional_eval_impl(Tag{}, std::forward<IfTrue>(t), std::forward<IfFalse>(f));
}

live demo

like image 177
krzaq Avatar answered Oct 12 '22 09:10

krzaq


In C++14 you could emulate if constexpr using generic lambda e.g. by:

#include <type_traits>
#include <iostream>

template <bool B>
struct constexpr_if {
   template <class Lambda, class T>
   static void then(Lambda l, T&& value) { }
};

template <>
struct constexpr_if<true> {
   template <class Lambda, class T>
   static void then(Lambda l, T&& value) {
      l(std::forward<T>(value));
   }
};


struct Banana {
   void peel() const {
     std::cout << "Banana::peel" << std::endl;
   }
};

struct Apple {
};

template <typename T>
void function(const T &param) {
    constexpr_if<std::is_same<T, Banana>::value>::then([&](auto &p){
       p.peel();
    }, param);
}

int main() {
  function(Banana{});
  function(Apple{});
}
like image 3
W.F. Avatar answered Oct 12 '22 09:10

W.F.