I want to use SFINAE to enable a particular template if the user passes a function pointer as a parameter.
I have googled around but found nothing - I also tried looking at the <type_traits> documentation but couldn't find anything that resembled a is_function_ptr<T>.
By function pointer, I mean global function pointers, like TReturn(*)(TArgs...).
Below is a type trait determining if something is a function pointer and a couple of test cases. Note, that to test if something is a function pointer, you need to test if std::is_pointer<P>::value is true and if std::is_function<T>::value is true where T is P with the pointer removed. The code below just does that:
#include <type_traits>
#include <iostream>
#include <utility>
template <typename Fun>
struct is_fun_ptr
: std::integral_constant<bool, std::is_pointer<Fun>::value
&& std::is_function<
typename std::remove_pointer<Fun>::type
>::value>
{
};
template <typename Fun>
typename std::enable_if<is_fun_ptr<Fun>::value>::type
test(Fun) {
std::cout << "is a function pointer\n";
}
template <typename Fun>
typename std::enable_if<!is_fun_ptr<Fun>::value>::type
test(Fun) {
std::cout << "is not a function pointer\n";
}
void f0() {}
void f1(int) {}
void f2(int, double) {}
struct s0 { void operator()() {} };
struct s1 { void operator()(int) {} };
struct s2 { void operator()(int, double) {} };
int main()
{
int v0(0);
int* p0(&v0);
void (*p1)() = &f0;
void (**p2)() = &p1;
std::cout << "v0="; test(v0);
std::cout << "p0="; test(p0);
std::cout << "p1="; test(p1);
std::cout << "p2="; test(p2);
std::cout << "f0="; test(&f0);
std::cout << "f1="; test(&f1);
std::cout << "f2="; test(&f2);
std::cout << "s0="; test(s0());
std::cout << "s1="; test(s1());
std::cout << "s2="; test(s2());
std::cout << "l0="; test([](){});
std::cout << "l1="; test([](int){});
std::cout << "l2="; test([](int, double){});
}
No SFINAE is needed to accept a function pointer or a member function pointer. To distinguish function objects from non-callable stuff SFINAE is needed, there's probably no way around this.
#include <utility>
#include <iostream>
template <typename Ret, typename... Parm>
void moo (Ret (*fp)(Parm...))
{
std::cout << "funptr" << std::endl;
}
template <typename Ret, typename Owner, typename... Parm>
void moo (Ret (Owner::*fp1)(Parm...))
{
std::cout << "memfunptr" << std::endl;
}
template <typename Funobj, typename... Parm,
typename Ret =
decltype((std::declval<Funobj>())
(std::forward(std::declval<Parm>())...))>
void moo (Funobj functor)
{
std::cout << "funobj" << std::endl;
}
void x1() {}
struct X2 { void x2() {} };
struct X3 { void operator()(){} };
int main()
{
moo(x1);
moo(&X2::x2);
moo(X3());
}
Related
In the example below I can effectively strip the const, volatile and reference qualifiers and use the single specialization for shared pointers. This is solved by the adding one more level of abstraction. How could I solve this without doing so? I could I just use the specialisations and match on shared_pointer, shared_pointer const etc?
#include <iostream>
#include <type_traits>
namespace detail {
template<typename T>
struct display;
template<typename T>
struct display<std::shared_ptr<T>> {
static void apply() {
std::cout << __FUNCTION__ << std::endl;
}
};
}
template<typename T>
void display() {
detail::display<std::remove_cvref_t<T>>::apply();
}
int main() {
std::shared_ptr<int> t;
display<decltype(t)>();
return 0;
}
So I have come up with a solution which I like much better which I thought I would share.
template<typename T>
struct is_shared_pointer : std::false_type { };
template<template<typename > typename T, typename U>
struct is_shared_pointer<T<U>> : std::is_same<std::decay_t<T<U>>, std::shared_ptr<U>> {};
template<typename T, typename Enable = void>
struct display;
template<typename T>
struct display<T, std::enable_if_t<is_shared_pointer<T>::value>> {
static void apply() {
std::cout << "shared ptr: " << __FUNCTION__ << std::endl;
}
};
template<typename T>
struct display<T, std::enable_if_t<std::is_integral_v<T>>> {
static void apply() {
std::cout << "integral :" << __FUNCTION__ << std::endl;
}
};
template<typename T>
struct display<T, std::enable_if_t<std::is_void_v<T>>> {
static void apply() {
std::cout << "void: " << __FUNCTION__ << std::endl;
}
};
template<typename T>
struct display<T, std::enable_if_t<std::is_floating_point_v<T>>> {
static void apply() {
std::cout << "floating: " << __FUNCTION__ << std::endl;
}
};
int main() {
std::shared_ptr<int> t;
display<decltype(t)>();
return 0;
}
That being said, I am open to suggestions, ideas and techniques.
A simplified example of my code:
namespace Example
{
class A
{
public:
template<typename T>
void foo(T &val);
template<>
void foo<long>(long &val);
};
template<typename T>
void A::foo(T &val)
{
std::cout << "Generic Function: " << val << std::endl;
}
template<>
void A::foo<long>(long &val)
{
std::cout << "Specialized Function: " << val << std::endl;
}
}
I get an error like:
explicit specialization in non-namespace scope
Why doesn't template specialization work here? How can I specify the behavior of a member function based on the type that's passed in?
You don't need specialization for this, simple overloading will work fine (ie, remove the template<> from the 2nd foo()), eg:
namespace Example
{
class A
{
public:
template<typename T>
void foo(T &val);
void foo(long &val);
};
template<typename T>
void A::foo(T &val)
{
std::cout << "Generic Function: " << val << std::endl;
}
void A::foo(long &val)
{
std::cout << "Specialized Function: " << val << std::endl;
}
}
Online Demo
Or, in C++17 and later, you can just get rid of the 2nd foo() altogether and use if constexpr inside the implementation of the 1st foo(), eg:
#include <type_traits>
namespace Example
{
class A
{
public:
template<typename T>
void foo(T &val);
};
template<typename T>
void A::foo(T &val)
{
if constexpr (std::is_same_v<T, long>)
std::cout << "Specialized Function: " << val << std::endl;
else
std::cout << "Generic Function: " << val << std::endl;
}
}
Online Demo
I want to handle noncopyable type by reference when SFINAE get unkown input, my code below can't work, is there a better way?
#include <iostream>
#include <functional>
template<typename T, typename std::enable_if<std::is_integral<T>::value, int>::type = 0>
void data_type(T const& t) {
std::cout << "integer" << std::endl;
}
void data_type(...) {
std::cout << "catch unknown" << std::endl;
}
int main() {
struct noncopyable_type {
int i;
noncopyable_type() {}
noncopyable_type(const noncopyable_type&) = delete;
};
int i;
noncopyable_type s;
// first try
data_type(i); // ok
data_type(s); // error: call to deleted constructor
// try again
data_type(std::cref(i)); // ok, but the type is std::reference_wrapper, not integer
data_type(std::cref(s)); // ok
}
There are probably many ways, this is the first one that came to mind. Live demo
#include <iostream>
#include <functional>
template <typename T,
typename = typename std::enable_if<std::is_integral<T>::value>::type>
void data_type(T const& t) {
std::cout << "integer" << std::endl;
}
template <typename ... T,
typename = typename std::enable_if<sizeof...(T)==1>::type>
void data_type(T const&...) {
std::cout << "unknown" << std::endl;
}
int main() {
struct noncopyable_type {
noncopyable_type() {}
noncopyable_type(const noncopyable_type&) = delete;
};
int i;
noncopyable_type s;
// first try
data_type(i); // ok
data_type(s); // ok
}
In C++17 I would just use if constexpr.
We rarely need to use the ... trick anymore. With concepts, we can get the overload resolution behaviour we need without having to play tricks with the parameter declaration clause:
template <typename T>
requires std::integral<T>
void data_type(T const& t) {
std::cout << "integer" << std::endl;
}
template <typename T>
void data_type(T const& t) {
std::cout << "unknown" << std::endl;
}
The first overload is more constrained than the second one, so the second one will only be used when the first one is not applicable due to its constraint not being satisfied.
Note that the first overload may equivalently be written like so:
template <std::integral T>
void data_type(T const& t) {
std::cout << "integer" << std::endl;
}
It's very unclear to me what the actual problem you're trying to solve is, but there's a few possibly better ways to approach this.
With if constexpr
template <typename T>
void data_type(T const&) {
if constexpr (std::is_integral_v<T>) {
std::cout << "integral\n";
} else {
std::cout << "unknown\n";
}
}
If (as I suspect) your goal is to not bind a reference to integral types (for whatever reason) you can get fancier with C++20 concepts
template <std::integral T>
void data_type(T) {
std::cout << "integral\n";
}
template <typename T> requires (!std::integral<T>)
void data_type(T const&) {
std::cout << "unknown\n";
}
I tried to implement template function specialization. You can run my tiny code in this fiddle. You can also see it below
#include <iostream>
#include <vector>
#include <list>
template <typename T>
struct is_vector {
static const bool value = false;
};
template <typename T>
struct is_vector<std::vector<T>> {
static const bool value = true;
using type = std::vector<T>;
};
template <typename T>
struct is_list {
static const bool value = false;
};
template <typename T>
struct is_list<std::list<T>> {
static const bool value = true;
using type = std::list<T>;
};
template<typename T, class = typename std::enable_if<is_list<T>::value>::type>
void foo(T t) {
std::cout << "is list" << std::endl;
}
/*
template<typename T, class = typename std::enable_if<is_vector<T>::value>::type>
void foo(T t) {
std::cout << "is vector" << std::endl;
}
*/
//The above code will cause an error, if we uncomment it
int main()
{
foo(std::list<int>{});
return 0;
}
In this code, I have several lines commented:
template<typename T, class = typename std::enable_if<is_vector<T>::value>::type>
void foo(T t) {
std::cout << "is vector" << std::endl;
}
If I uncomment it, I get "redifinition" error. I'm not sure how to fix it.
If I uncomment it, I get "redifinition" error. I'm not sure how to fix it.
The reason is quite simple: default values for template type arguments are not a part of a function signature. It means that you have the same template defined two times.
You might move SFINAE part in to the function return type, as it is suggested by other answers, or change the code to:
template<typename T, std::enable_if_t<is_list<T>::value, int> = 0>
void foo(T t) {
std::cout << "is list" << std::endl;
}
template<typename T, std::enable_if_t<is_vector<T>::value, int> = 1>
void foo(T t) {
std::cout << "is vector" << std::endl;
}
You can do this instead.
template<typename T>
typename std::enable_if<is_list<T>::value>::type foo(T t) {
std::cout << "is list" << std::endl;
}
template<typename T>
typename std::enable_if<is_vector<T>::value>::type foo(T t) {
std::cout << "is vector" << std::endl;
}
Not sure if this is what you are after, but you could just check if either list or vector is a matching type:
template<typename T, class = typename std::enable_if<is_list<T>::value || is_vector<T>::value>::type>
void foo(T t) {
std::cout << "is list" << std::endl;
}
Updated fiddle: https://godbolt.org/g/oD3o9q
Update (for C++14):
template<typename T, class = std::enable_if_t<is_list<T>::value || is_vector<T>::value>>
void foo(T t) {
std::cout << "is list" << std::endl;
}
I want to unpack the parameter pack in func (see line A), but it doesnt work. How can I unpack inside func< > or modify Line A only?
#include <iostream>
using namespace std;
void func()
{
cerr << "EMPTY" << endl;
}
template <class A, class ...B> void func()
{
cerr << "A: " << endl;
func<B... >(); // line A
}
int main(void)
{
func<int,int>();
return 0;
}
An expected output :
A:
A:
edited:
all of answers are very good. thanks alot
Sometimes it's easier to unpack everything at once, instead of recursively. If you simply want a parameter pack for_each, you can use a variant of the braced-init-list expansion trick (Live demo at Coliru):
template <class A>
void process_one_type() {
cerr << typeid(A).name() << ' ';
}
template <class ...B> void func()
{
int _[] = {0, (process_one_type<B>(), 0)...};
(void)_;
cerr << '\n';
}
By using func<B... >(); you are implying that func is a function template, but your previously defined func() is not.
You need to define a func() template that accepts zero template arguments. Here's a working example (on g++ 4.8.1):
#include <iostream>
using namespace std;
void func()
{
cerr << "EMPTY" << endl;
}
template <class ... B>
typename std::enable_if<sizeof...(B) == 0>::type func()
{
}
template <class A, class ...B> void func()
{
cerr << "A: " << endl;
func<B... >(); // line A
}
int main(void)
{
func(); // This outputs EMPTY
func<int,int>(); // This will not output EMPTY
return 0;
}
Try this:
template <class A> void func()
{
cerr << "A: " << endl;
}
template <class A, class B, class ...C> void func()
{
cerr << "A: " << endl;
func<B, C...>(); // line A
}
Consider what the invocation of the recursive call func<B...>(); looks like when B... is empty. It's calling func<>(); but the definition of your attempted base case func() is not a template function, ie. you can't call it via func<>();
Since we don't have partial specialization for function templates yet, (hopefully it will be supported soon) one way to do it is to use a class template to do the partial specialization and use the function to simply delegate the work to the class template.
#include <iostream>
/* Forward declaration. */
template <typename... T>
struct FuncImpl;
/* Base case. */
template <>
struct FuncImpl<> {
void operator()() const {
std::cout << "Base case" << std::endl;
}
}; // FuncImpl<>
/* Recursive case. */
template <typename First, typename... Rest>
struct FuncImpl<First, Rest...> {
void operator()() const {
std::cout << "Recursive case" << std::endl;
FuncImpl<Rest...>()();
}
}; // FuncImpl<First, Rest...>
/* Delegate function. */
template <typename... T>
void Func() {
FuncImpl<T...>()();
}
int main() {
Func<>();
Func<int, double>();
}
Personally I think this solution is cleaner than other solutions such as tagged dispatching or SFINAE, despite the cruft around operator()s.