Skip to main content

Binding std::function to member functions

I realized that std::function can be bound to member functions without requiring the *this object. Consider the following examples.
// std::string::empty is a const function. All variables from e1 to e5 are fine.
std::function<bool(std::string)> e1 = &std::string::empty;
std::function<bool(std::string &)> e2 = &std::string::empty;
std::function<bool(const std::string &)> e3 = &std::string::empty;
std::function<bool(std::string *)> e4 = &std::string::empty;
std::function<bool(const std::string *)> e5 = &std::string::empty;

// std::string::push_back is not a const function. p4 and p5 don't compile.
std::function<void(std::string, char)> p1 = &std::string::push_back;
std::function<void(std::string &, char)> p2 = &std::string::push_back;
std::function<void(std::string *, char)> p3 = &std::string::push_back;

// These two don't compile because push_back is a non-const function 
std::function<void(const std::string &, char)> p4 = &std::string::push_back;
std::function<void(const std::string *, char)> p5 = &std::string::push_back;
I thought I knew how to do that but this time I found that the syntax is a little different than what I had in mind.

I used tho think (incorrectly) that just like function types for free-standing functions, one would create a member-function type. While, it's straight-forward to create function types for free-standing functions, I think it's not possible to create member function types. Don't get me wrong. One can create pointer-to-member-function type just like pointer-to-a-function type. Here's what I mean.
using F = int(const char *); // a function type of a free standing function.
F f = std::atoi; // error because a there are no instances of a function type.
F* fnew = new F(); //error because new can't be applied to a function type.
F* fptr = &std::atoi; // OK. A pointer to a function type is initialized.

// However, there's no function type for member functions
using GPTR = bool (std::string::*)() const; // OK. This is a pointer-to-member-function-type. 
GPTR gptr = &std::string::empty; // OK. This is a pointer to a member function.
string s;
std::cout << (s.*gptr)(); // OK. prints 1
using H = decltype(*gptr); // error. It makes no sense without a std:string object. Illformed.

bool (*x)(const std::string &) = gptr; // error. Incompatible types.
std::function<bool (const std::string &)> fobj = gptr; // OK! Neat!
Therefore, std::function uses the syntax of free-standing function types to bind to pointer to member functions. That's neat.

Comments

Popular posts from this blog

Multi-dimensional arrays in C++11

What new can be said about multi-dimensional arrays in C++? As it turns out, quite a bit! With the advent of C++11, we get new standard library class std::array. We also get new language features, such as template aliases and variadic templates. So I'll talk about interesting ways in which they come together.

It all started with a simple question of how to define a multi-dimensional std::array. It is a great example of deceptively simple things. Are the following the two arrays identical except that one is native and the other one is std::array?

int native[3][4];
std::array<std::array<int, 3>, 4> arr;

No! They are not. In fact, arr is more like an int[4][3]. Note the difference in the array subscripts. The native array is an array of 3 elements where every element is itself an array of 4 integers. 3 rows and 4 columns. If you want a std::array with the same layout, what you really need is:

std::array<std::array<int, 4>, 3> arr;

That's quite annoying for two r…

Understanding Fold Expressions

C++17 has an interesting new feature called fold expressions. Fold expressions offer a compact syntax to apply a binary operation to the elements of a parameter pack. Here’s an example. template <typename... Args> auto addall(Args... args) { return (... + args); } addall(1,2,3,4,5); // returns 15. This particular example is a unary left fold. It's equivalent to ((((1+2)+3)+4)+5). It reduces/folds the parameter pack of integers into a single integer by applying the binary operator successively. It's unary because it does not explicitly specify an init (a.k.a. identity) argument. So, let add it. template <typename... Args> auto addall(Args... args) { return (0 + ... + args); } addall(1,2,3,4,5); // returns 15. This version of addall is a binary left fold. The init argument is 0 and it's redundant (in this case). That's because this fold expression is equivalent to (((((0+1)+2)+3)+4)+5). Explicit identity elements will come in handy a little la…

Folding Monadic Functions

In the previous two blog posts (Understanding Fold Expressions and Folding Functions) we looked at the basic usage of C++17 fold expressions and how simple functions can be folded to create a composite one. We’ll continue our stride and see how "embellished" functions may be composed in fold expressions.

First, let me define what I mean by embellished functions. Instead of just returning a simple value, these functions are going to return a generic container of the desired value. The choice of container is very broad but not arbitrary. There are some constraints on the container and once you select a generic container, all functions must return values of the same container. Let's begin with std::vector.
// Hide the allocator template argument of std::vector. // It causes problems and is irrelevant here. template <class T> struct Vector : std::vector<T> {}; struct Continent { }; struct Country { }; struct State { }; struct City { }; auto get_countries…