# The Evolution of Functions in Modern C++

Let’s start briefly with what he had before “modern” times.

# Pre-C++11

Functions were available since the beginning of C++, whose first variant was called C with classes. This is how a function looks:

```int add(int a, int b)
{
return a + b;
}```

This is what we call a non-member function or a free function, because it does not belong to any class. There are also member functions, that are part of a class/struct. These are also referred as methods (like in most other object-oriented programming languages), although this term is not used anywhere in the C++ standard. Here is an example:

```class math
{
public:
{
return a + b;
}
};```

There are multiple kinds of functions, including the following:

```int add(int a, int b) {return a + b;}
double add(double a, double b) {return a + b;}```
• static functions
```static int add(int a, int b) {return a + b;}

struct math
{
static int add(int a, int b) {return a + b;}
}```
• inline functions
```inline int add(int a, int b) {return a + b;}

struct math
{
inline int add(int a, int b);
}

int match::add(int a, int b) {return a + b;}```
• operators
```std::string operator+(std::string const & txt, int n)
{
return txt + std::to_string(n); // channels your JavaScript energy
}```
• constant member functions
```class wrapper
{
public:
wrapper(int a): value_(a) {}
int get() const {return value_;}
private:
int value_;
};```
• virtual member functions
```struct A
{
virtual void f() { std::cout << "A::f()\n"; }
};

struct B : public A
{
virtual void f() { std::cout << "B::f()\n"; }
};```
• special class functions (default constructor, copy-constructor, copy-assignment operator, and destructor)
```class wrapper
{
public:
wrapper() : value_(0) {}
wrapper(wrapper const & other) {value_ = other.value_; }
wrapper& operator=(wrapper const & other) {if(this != &other) {value_ = other.value_;} }
~wrapper() {}
private:
int value_;
};```

All these are very simple examples but the point here is not to detail all these features that existed before modern C++. One thing that is missing here, though, is templates. Templates are blueprints that define families of functions or classes. The compiler instantiates actual overloads (in the case of function templates) from their use. Here is an example:

```template <typename T>
{
return a + b;
}```

Now that we’ve briefly looked at these, let’s see what changes modern C++ brought.

# C++11

These are function templates with a variable number of arguments.

```template <typename T>
{
return a + b;
}

template <typename T, typename ...Ts>   // 
T add(T t, Ts ... rest)                 // 
{
return t + add(rest...);            // 
}```

The ellipsis (`...`) defines a parameter pack. We can have:

• a template parameter pack, such as `typename ... Ts` at line 
• a function parameter pack, such as `Ts ... rest` at line 
• a pack expansion, such as `add(rest...)` at line 

## Alternative function syntax

The return type of a function can be placed at the end of the function declaration, after the `->` token:

```auto add(int a, int b) -> int
{
return a + b;
}```

In C++11, this is not of much help for non-template functions, but it’s important for some function templates. Consider a version of `add()` that takes arguments of different types:

```template<typename T, typename U>
??? add(T const & a, U const & b)
{
return a + b;
}```

What should the return type be? With the alternative function syntax we can place the return at the end of the expression and specify it with a `decltype` expression:

```template<typename T, typename U>
auto add(T const & a, U const & b) -> decltype(a + b)
{
return a + b;
}```

## constexpr functions

These are functions that can be evaluated at compile-time. The result of evaluating such a function is a compile-time value that can be used anywhere compile-time values are required. To make a function constexpr you need to define it with the `constexpr` keyword, such as in the following example:

```template <typename T>
constexpr T add(T a, T b)
{
return a + b;
}

int main()
{
int arr[add(1,2)] = {1,2,3};    // 

int a, b;
std::cin >> a >> b;
std::cout << add(a, b) << '\n';  // 
}```

Just because a function is declared `constexpr`, doesn’t mean it is evaluated at compile-time. In the above example:

• the first call to `add` is evaluated at compile-time (line ) because all its arguments are integer literals
• the second call to `add` (at line ) is evaluated at runtime because its arguments are only know at runtime

## Override and final specifiers for virtual functions

These new specifies help us better describe virtual functions in derived classes.

The `override` specifier used on a virtual function tells the compiler it is an overridden function of a base class virtual function. If the signature does not match, the compiler triggers an error.

```struct A
{
virtual void f(int) {}
virtual void g() {}
};

struct B : public A
{
void f(int) override {}  // OK
void g(char) override {} // error, g() does not override anything
};```

The `final` specifier tells a compiler a virtual function can longer be overridden in a derived class.

```struct A
{
virtual void f() {}
};

struct B : public A
{
void f() override final {}
};

struct C : public B
{
void f() override {}   // error, f cannot be overridden anymore
};```

It should be mentioned that the `final` specifier can also be used on classes, in which case it prevents a class from being further derived.

## More special member functions

Move semantics are not easy to describe in one sentence. Basically, it’s a language feature that enables the transfer of ownership of a resource from one object to another. Their purpose is improving performance by avoiding copies of resources that are not really necessary. For classes, these bring two new special functions: move constructor and move assignment operator:

```struct buffer
{
buffer()                       // default constructor
:data_(nullptr), size_(0)
{}

explicit buffer(size_t size)   // constructor
:data_(new char[size]), size_(size)
{}

~buffer()                      // destructor
{
delete [] data_;
}

buffer(buffer const & other)   // copy constructor
: data_(new char[other.size_])
, size_(other.size_)
{
std::memcpy(data_, other.data_, size_);
}

buffer& operator=(buffer const & other) // copy assignment operator
{
if(this != &other)
{
delete [] data_;
data_ = new char[other.size_];
size_ = other.size_;
std::memcpy(data_, other.data_, size_);
}

return *this;
}

buffer(buffer&& other)           // move constructor
: data_(std::move(other.data_))
, size_(other.size_)
{
other.data_ = nullptr;
other.size_ = 0;
}

buffer& operator=(buffer&& other) // move assignment operator
{
if(this != &other)
{
delete [] data_;
data_ = std::move(other.data_);
size_ = other.size_;
other.data_ = nullptr;
other.size_ = 0;
}

return *this;
}

private:
char* data_;
size_t size_;
};

int main()
{
buffer b1;
buffer b2(10);
buffer b3 = b2;
buffer b4 = std::move(b3);
}```

## Default and deleted functions

The special member functions (see above) can be generated by the compiler. However, this does not happen in some circumstances. For instance, if any user-defined constructor exists, a default constructor is not generated, or if a move constructor or move assignment operator is defined, then no copy constructor and copy assignment operator is generated. Rather than implementing these by yourself you can explicitly ask the compiler to generate the default implementation, using the `= default` specifier.

```struct foo
{
foo(int) {}      // user-defined constructor
foo() = default; // compiler generated default constructor
};```

On the other hand, sometimes we need some functions or some function overloads to not be available. We can prevent a function from being called by defining it with the `= delete` specifier:

```struct noncopyable
{
noncopyable() = default;
noncopyable(noncopyable const &) = delete;
noncopyable& operator=(noncopyable const &) = delete;
};```

Any function can be deleted, not just member functions, or special member functions (as shown in the previous example).

```template <typename T>
{
return a + b;
}

template <>
int add<int>(int a, int b) = delete;

int main()
{
add(1, 2); // error, this specialization is deleted
}```

## Lambdas

Lambdas are not really functions in C++ and the term lambda function is incorrect. The right term is lambda expressions. Lambdas are syntactic sugar for creating unnamed function objects (which can capture variables in scope). A function object is a class with an overloaded call operator.

```int main()
{
auto add = [](int a, int b) { return a + b; };
}```

The compiler would generate something as follows (conceptually, as the details may vary):

```int main()
{
class __lambda_1_10
{
public:
inline int operator()(int a, int b) const
{
return a + b;
}
};

}```

Lambdas are useful for encapsulating a few lines of code that are then passed to functions such as general purpose algorithms or asynchronous functions.

```int main()
{
std::vector<int> v {1, 5, 9, 2, 7};

std::sort(v.begin(), v.end(), [](int a, int b){return a > b;}); // sorts descending

for(const auto & e : v)
std::cout << e << '\n';
}```

# C++14

## Function return type deduction

The alternative function syntax with trailing return type got simplified in C++14 with the compiler being able to deduce the return type from the return expression(s) present in the body of a function. Therefore, functions can be simplified as follows:

```auto add(int a, int b)
{
return a + b;
}```

Again, this is more useful in template code:

```template <typename T, typename U>
{
return a + b;
}```

## Generic lambdas

A generic lambda is a lambda expression with at least one parameter specified with the `auto` specifier.

```int main()
{
using namespace std::string_literals;

auto add = [](auto a, auto b) {return a + b;};

}```

This has the effect that the anonymous structure generated by the compiler has a template function call operator. For the above example, it would look, at least conceptually, as follows:

```int main()
{
using namespace std::string_literals;

class __lambda_8_16
{
public:
template <typename T0, typename T1>
inline auto operator()(T0 a, T1 b) const
{
return a + b;
}

template<>
inline int operator()(int a, int b) const
{
return a + b;
}

template<>
inline double operator()(double a, double b) const
{
return a + b;
}

template<>
inline std::string operator()(std::string a, std::string b) const
{
return std::operator+(a, b);
}
};

}
```

# C++20

## Immediate functions

Constexpr functions from C++11 can be evaluated either at compile-time (if all arguments are compile-time values) or runtime. C++20 adds a new categories of functions, called immediate functions, that must be evaluated at compile-time. They always produce a compile-time expression and they are always visible only at compile-time. Symbols are not emitted for these functions, you cannot take the address of such functions, and tools such as debuggers will not be able to show them.

These functions are defined using the new `consteval` keyword. Here is an example:

```consteval int add(int const a, int const b)
{
return a + b;
}

int main()
{
constexpr int s1 = add(1, 2);   // OK, compile-time evaluation
int a = 12, b = 66;
const int s2 = add(a, b);       // error

using fptr = int(int, int);
}```

`consteval` specifier implies `inline`. A function that is `consteval` is a `constexpr` function, and must satisfy the requirements applicable to `constexpr` functions (or `constexpr` constructors).

## Abbreviated function templates

If you find template syntax ugly or difficult this feature is for you. It allows you to write function templates without using template syntax. Instead, you use the auto specifier to define function parameters. A function with a least one parameter specified with the auto specifier is an abbreviated function template:

```auto add(auto a, auto b)
{
return a + b;
}```

The compiler transforms this into a function template:

```template <typename T, typename U>
{
return a + b;
}```

These are actually called unconstrained abbreviated function templates because there are no constraints on the template arguments. However, you can specify constraints with the help of concepts. Such functions are called constrained abbreviated function templates.

```auto add(std::integral auto a, std::integral auto b)
{
return a + b;
}```

This is the same as follows:

```template <std::integral T, std::integral U>
{
return a + b;
}```

## Lambda templates

The generic lambdas in C++14 have some shortcomings. For instance, consider this lambda:

`auto add = [](auto a, auto b) {return a + b;};`

The compiler generates the following function object:

```struct _lambda_1
{
template <typename T0, typename T1>
inline auto operator()(T0 a, T1 b) const
{
return a + b;
}
};```

But what if the intention is that the two arguments, `a` and `b`, to be of the same type? There is no way to model that in C++14. For this reason, C++20 introduces lambda template, that allows us to define generic lambdas using template syntax:

`auto add = []<typename T>(T a, T b) {return a + b;};`

## constexpr virtuals

You heard it right: in C++20, virtual functions can be defined as constexpr:

```struct magic
{
constexpr virtual int def() const { return 0; }
};

struct programming_magic : public magic
{
constexpr int def() const override { return 42; }
};

constexpr int initval(magic const & m)
{
return m.def() + 1;
}

int main()
{
constexpr programming_magic pm;
int arr[initval(pm)] = {0};
}```

This doesn’t seem to have too many use-cases. I don’t see where we can use this too much, but it’s now possible.

## Coroutines

This one is one of the major features of the C++20 standard. A coroutine is a function that has the ability to be suspended and resumed. Unfortunately, C++20 only defines a framework for the execution of coroutines, but does not define any coroutine types satisfying such requirements. That means, we need to either write our own or rely on 3rd party libraries for this. Such a library is the cppcoro library.

In C++20, there are three new keywords, for coroutines: `co_await`, `co_return`, and `co_yield`. A function becomes a coroutine if it uses one of these three:

• the `co_await` operator to suspend execution until resumed
• the `co_return` keyword to complete execution and optionally return a value
• the `co_yield` keyword to suspend execution and return a value

Here is an example of a producer-consumer scenario (a coroutine produces new values and another coroutine consumes them as they become available):

```#include <cppcoro/generator.hpp>

cppcoro::generator<std::string> produce_items()
{
while (true)
{
auto v = rand();
using namespace std::string_literals;
auto i = "item "s + std::to_string(v);
print_time();
std::cout << "produced " << i << '\n';
co_yield i;
}
}```
```#include <cppcoro/task.hpp>

{
int i = 1;
for(auto const& s : produce_items())
{
print_time();
std::cout << "consumed " << s << '\n';
if (++i > n) break;
}

co_return;
}```

1. Sergey Belov says: