C++ Standard Library Function Helpers

An introduction to a collection of standard library utilities that can help us when working with first class functions
This lesson is part of the course:

Professional C++

Comprehensive course covering advanced concepts, and how to use them on large-scale projects.

32.jpg
Ryan McCombe
Ryan McCombe
Posted

One final problem remains with our implementation. The issue is that the every function within our class is much too specific in the type of argument it receives.

We want our function to work with any type of predicate, whether a pointer, a function object, or a lambda.

class Party {
public:
  // This is too restrictive
  using Handler = bool (*)(const Character&);
  bool every(Handler Predicate) {
    return (
      Predicate(*PlayerOne) &&
      Predicate(*PlayerTwo) &&
      Predicate(*PlayerThree) &&
      Predicate(*PlayerFour)
    );
  }
// The rest of the class is unchanged
};

There are a few ways to solve this. The first is to make every a template function. Here, we use the shorthand method, simply specifying the argument type as auto:

class Party {
public:
  bool every(auto Predicate) {
    return (
      Predicate(*PlayerOne) &&
      Predicate(*PlayerTwo) &&
      Predicate(*PlayerThree) &&
      Predicate(*PlayerFour)
    );
  }
// The rest of the class is unchanged
};

This solves our problem, although our function is now a little too open, potentially accepting any type of argument. We can be more specific using concepts.

std::invocable

The standard library can help us out with the std::invocable concept, which is available after we include <concepts>:

#include <concepts>

// A function that accepts an integer
std::invocable<int>

// A function that accepts two integers
std::invocable<int, int>

// A function that accepts a const Character ref
std::invocable<const Character&>

This concept doesn’t let us specify the return type of the function. We could create our own using the techniques we learned in the templates lessons. But, in this case, we don’t need to:

std::predicate

When our function is expected to return a bool, the standard library already comes with std::predicate to help us out, also within <concepts>:

#include <concepts>

// A function that accepts an
// integer and returns a bool
std::predicate<int>

// A function that accepts two
// integers and returns a bool
std::predicate<int, int>

// A function that accepts a const
// Character ref and returns a bool
std::predicate<const Character&>

Let’s update our every function to make use of this. Once we’ve included <concepts>, our function template can look like this:

class Party {
public:
  bool every(
    predicate<const Character&> Predicate
  ) {
    return (
      Predicate(*PlayerOne) &&
      Predicate(*PlayerTwo) &&
      Predicate(*PlayerThree) &&
      Predicate(*PlayerFour)
    );
  }
// The rest of the class is unchanged
};

std::function

There is another way we can define the types of function-like things, and that is through the std::function helper.

This is available by including <functional>, and the syntax to use it looks like this:

#include <functional>

// A function that returns nothing
// and accepts no arguments
std::function<void()> Example1;

// A function that returns a bool
// and accepts an int argument
std::function<bool(int)> Example2;

// A function that returns a bool and
// accepts two int arguments
std::function<bool(int, int)> Example3;

// A function that returns a bool and
// accepts a const Character ref
std::function<bool(const Character&)> Example4;

If we’re initializing such a variable while we create it, the compiler can figure out what the template parameters are. Because of this, we do not need to provide them. This is an example of class template argument deduction (CTAD):

bool MyFunction(int x, int y) {
  return true;
}

// These two approaches are equivalent
std::function<bool(int, int)> MyVariable1 {
  MyFunction
};
std::function MyVariable2 { MyFunction };

We can now update our original every function to use this technique. This is an alternative to our previous examples where we used templates and concepts directly.

Assuming we’ve included <functional>, we can update our code to look like this:

using Handler = std::function<bool(const Character&)>;

class Party {
public:
  bool every(Handler Predicate) {
    return (
      Predicate(*PlayerOne) &&
      Predicate(*PlayerTwo) &&
      Predicate(*PlayerThree) &&
      Predicate(*PlayerFour)
    );
  }
// The rest of the class is unchanged
};

Was this lesson useful?

Ryan McCombe
Ryan McCombe
Posted
This lesson is part of the course:

Professional C++

Comprehensive course covering advanced concepts, and how to use them on large-scale projects.

Working with Functions
7a.jpg
This lesson is part of the course:

Professional C++

Comprehensive course covering advanced concepts, and how to use them on large-scale projects.

Free, unlimited access!

This course includes:

  • 106 Lessons
  • 550+ Code Samples
  • 96% Positive Reviews
  • Regularly Updated
  • Help and FAQ
Next Lesson

C++ Binding and Currying

Learn to create functions from existing functions, by providing only some of the arguments.
5c.jpg
Contact|Privacy Policy|Terms of Use
Copyright © 2023 - All Rights Reserved