I want to use SFINAE to create a templated member function which takes a Consumer
functor. Whether something is a consumer depends on a templated static constexpr bool isConsumer
member variable. I have simplified my code down to the following example:
#include <type_traits>
template <typename T>
struct Container {
T data[100];
template <typename Consumer>
static constexpr bool isConsumer = std::is_invocable_r_v<void, Consumer, T>;
template <typename Consumer, std::enable_if_t<isConsumer<Consumer>, int> = 0>
void forEach(const Consumer &consumer);
};
template <typename T>
template <typename Consumer, std::enable_if_t<Container<T>::template isConsumer<Consumer>, int>>
void Container<T>::forEach(const Consumer &consumer)
{
for (int i = 0; i < 100; ++i) {
consumer(data[i]);
}
}
This does not compile for reasons that I don't comprehend:
<source>:16:20: error: out-of-line definition of 'forEach' does not match any declaration in 'Container<T>'
void Container<T>::forEach(const Consumer &consumer)
^~~~~~~
It does compile just fine when I inline isConsumer
, as in, use std::is_invocable_r_v
directly. I would like to avoid this because in my real code the signature of the Consumer
is quite complicated and this requires quite a bit of copy/paste.
Pulling isConsumer
outside the class is not an option either, because in my real code it depends on private typedefs inside Container
. It must be inside the class.
How do I use std::enable_if
correctly here?
static_assert
, but then I get error messages inside of my container code instead of at the call site, which is irritating. The third solution looks good though. – Pecksniffian