I wonder, why functional objects in c++ are implemented as templated, with void
as default type since c++14.
For example:
This object in fact performs arithmetic operation +
, -
, *
, /
, when called by operator()
.
The operator()
has to be template to work with different types as arguments, but why does the struct have to be?
EDIT
I can create an operator std::plus<>
, which may work with a different types in operator()
:
struct Foo{
int foo;
};
Foo operator+(const Foo& lhs, const Foo& rhs){
return {2 * lhs.foo + 3 * rhs.foo};
}
std::ostream& operator<<(std::ostream& os, const Foo& f){
std::cout << f.foo;
return os;
}
int main()
{
auto op = std::plus<>();
std::cout << op(5, 3) << "\n";
std::cout << op(3.14, 2.71) << "\n";
std::cout << op(Foo(2), Foo(3)) << "\n";
}
And this gives the expected output. Or it may be the case, that having specified the type initially you get something more optimized?
It's a design choice. If you specify the type there is no template operator()
, instead the whole class is a template. The operator()
is simply something like
constexpr T operator()(const T &lhs, const T &rhs) const
{
return lhs + rhs;
}
This is in several ways different from having a template operator()
.
If we pass a std::plus<int>
it's a plus functor for specifically int
s and nothing else.
If we instead passed a std::plus<>
without specifying the type it would have a templated operator()
. That functor could apply it's operator()
to any valid type.
Some advantages with restricting the type from the top of my head:
Since the type is specified the functor can deal with implicit conversion without any issues.
You know for a fact that the functor is not going to silently do things we don't want it to. It's only ever going to do addition on T
s.
Edit
Some examples when the behaviour would differ.
#include <iostream>
#include <functional>
#include <string>
struct Foo {};
int main()
{
auto stringadd = std::plus<std::string>{};
auto anyadd = std::plus<>{};
std::cout << stringadd("hey ", "you") << '\n';
//std::cout << anyadd("hey ", "you") << '\n'; // error: no match for call to '(std::plus<void>) (const char [5], const char [4])'
//std::cout << stringadd("hey ", 1) << '\n'; // error: no match for call to '(std::plus<std::__cxx11::basic_string<char> >) (const char [5], int)'
std::cout << anyadd("hey ", 1) << '\n';
}