Functors: templated struct vs templated operator() - c++

The usual pattern for standard library function objects is to have a templated struct with a non-template operator(). For example, std::less looks something like so:
template <typename T>
struct less
{
bool operator()(const T& lhs, const T& rhs) const {
return lhs < rhs;
}
};
std::vector<float> vec = ...;
std::sort(vec.begin(), vec.end(), less<float>{});
My question is, why is this better than a non-template struct with a templated operator()? It seems that the above functor would be equivalent in operation to:
struct less2
{
template <typename T>
bool operator()(const T& lhs, const T& rhs) const {
return lhs < rhs;
}
};
std::vector<float> vec = ...;
std::sort(vec.begin(), vec.end(), less2{});
except that we get the bonus of automatic type deduction. Even better, if we wanted to, we could compare different types, provided it made sense to do so:
struct less
{
template <typename T, typename U>
bool operator()(const T& lhs, const U& rhs) const {
return lhs < rhs; // compile error if operator<(T, U) is not defined
}
};
and from there it's obvious how we could use decltype to get a truly generic std::plus, for example. But the standard library doesn't do it that way.
Of course, I'm sure that I'm not the first person that this has occurred to, and I'm sure that there's a very good reason that the standards committee decided to go with the first pattern and not the second. I'm just not sure what it is. Can any of the gurus enlighten me?

When the original functors were created none of the needed language facilities (return type deduction, perfect forwarding) did exist to solve the problem. The current design also has the benefit of allowing users to specialize the functors for their own types, even if this should strictly not be necessary.
C++1y introduces void specializations for all the functors (and uses void as a default argument) to make them easier to use. They will deduce and perfectly forward arguments and return types. This will also allow heterogeneous comparisons.
You will be able to write code like:
std::sort(begin(v), end(v), less<>());
The paper introducing this change is N3421.

Related

Concept polymorphism in C++

The short question is: how to make templated function behave differently, based
on parameter concept support.
I mean, if type T implements some concept, then my function should process it specially, than other types, that doesn't.
The question extension is: how this function should be declared, so end-user can see what types it supports in its declaration (user, shouldn't check the defintion in order to obtain this knowledge).
So that's my concrete example, I use the "concepts" idea as it's presented in "The C++ Programming Language" by B. Stroustrup, namely a constexpr predicates, instead of c++20's concepts, since my compiler doesn't support it, yet:
template<typename T>
bool constexpr is_matrix() { ... }
template<size_t N, size_t M, typename T>
class Matrix {
...
template<typename U>
Matrix& operator+=(const U& v) {
if constexpr (is_matrix<U>()) {
// handle matrix case
} else {
// handle scalar case
}
}
...
}
This example is taken from my simple Matrix/Vector lib, that I use to study software rendering. My idea here is to require type U to satisfy my concept (support all necessary operations, provide necessary type aliases) instead of requiring it being my Matrix type, when it fails that check it is supposed to be handled as scalar.
So what techniques, can be applied here in order to make this code clearer for end-user, and are there better ways to provide concept-based parametric polymorphism, than constexpr if?
My only solution to this problem, that I was able to come up with was usage of enable_if, like this:
...
template<typename U, typename =
enable_if_t<is_convertible_v<U, T> ||
(is_matrix<U>() && is_convertible_v<matrix_value_type_t<U>, T>)>>
Matrix& operator+=(const U& v) {
...
Which is quite verbose and can't be called pretty, btw I would prefer to static assert that type should be either convertible or being matrix of convertible values instead of hiding this operator.
EDIT: On second thought about my solution, I actually could static_assert in the definition, but still providing assertion criteria in declaration
template<typename U, bool check = is_convertible_v<U, T> || (is_matrix<U>() && is_convertible_v<matrix_value_type_t<U>, T>)>
... {
static_assert(check, "Type U should be either convertible to T, or being a matrix of convertible values");
}
EDIT2: Which further can be improved into more readable variant:
...
template <typename U, bool check = std::disjunction_v<
compatible_type<This, U>,
compatible_matrix<This, U>,
compatible_vector<This, U>>>
Matrix& operator+=(const U& v) {
assert_compatible<check>();
...
Simply overload the operator for all types you want to support. You may also add another version that generates a comprehensible compile-time error
Matrix& operator+=(Matrix const&);
template<typename matrix, typename=enable_if_t<is_matrix<matrix>::value>>
Matrix& operator+=(matrix const&);
template<typename scalar, typename=enable_if_t<is_scalar<scalar>::value>>
Matrix& operator+=(scalar);
template<typename arg>
Matrix& operator+=(arg&&) // catch attempts to add wrong argument type
{
static_assert(is_matrix<arg>::value || is_scalar<arg>::value,
"Matrix::operator+=(arg) requires matrix or scalar argument");
assert(false);
return*this;
}
You may also declare the last operator [[noreturn]].

why is std::plus a class template?

The C++ functor std::plus is implemented like
template<typename T>
struct plus
{
constexpr T operator+(const T& lhs, const T& rhs) const
{ return lhs+rhs; }
};
but there is also the specialisation
template<>
struct plus<void>
{
template< class T, class U>
constexpr auto operator()(T&& lhs, U&& rhs) const
-> decltype(std::forward<T>(lhs) + std::forward<U>(rhs))
{ return std::forward<T>(lhs) + std::forward<U>(rhs); }
};
which has the advantage that it can operate on any types, even differnt ones, as long as T+U is defined (for example std::string and const char*).
I was wondering why struct std::plus is not defined as a non-template with the functionality of the existing std::plus<void>? Are there any possible applications which could not be served by such an implementation?
Of course, this could have historical reasons (so that it cannot be changed). But if this is the only reason, wouldn't it be possible to change the template argument to default to void?
template<typename T=void> struct plus;
edit. I just realised that according to cppreference plus<T> defaults to plus<void> since C++14.
Arguably, plus<void>::operator+ could not have been implemented in C98++, but the following could
struct plus
{
template<class T>
T operator()(const T&lhs, const T&rhs) const
{ return lhs + rhs; }
};
which has exactly the same functionality as plus<T>, but avoids the template. So why was this not chosen instead? What was the logic/motivation behind the class template? (it violates the rule "do it simple if possible")
Templated member functions were not allowed in early versions of C++ (even those which allowed templated classes and free functions). That meant that functors like std::plus had to be fully specialized, rather than deferring specialization to the operator.
While I have already been penalized for second-guessing the reasons today, I will still dare to coin my view :)
I would assume, std::plus was made a template so that it could be speciailized and something else other than + be used for types which do not have + defined on them.

Policy to produce the result type of an arithmetic operation?

Consider the following example:
#include <iostream>
#include <string>
template <class Property>
struct MyClass
{
double _data;
};
template <class Property>
inline MyClass<Property> operator+(const MyClass<Property> lhs,
const MyClass<Property> rhs)
{
return {lhs._data + rhs._data};
}
int main()
{
MyClass<std::string> a{1.5};
MyClass<std::string> b{2.5};
MyClass<std::string> c = a + b;
std::cout<<c._data<<std::endl;
return 0;
}
It is simple and does not exhibit any problem of design (at least I think). Now consider that I want to be able to generate an operator+ (and all arithmetic operators) for classes with different property types. But there is no standard way to decide whether MyClass<Property1> + MyClass<Property2> should be a MyClass<Property1> or a MyClass<Property2>: so the good choice should be specified by the user. The question is the following: how to redesign the class (or anything else) to let the user provide a conversion strategy ? (I mean how would it be designed in the standard library or in boost ?)
EDIT: To clarify, the result type of MyClass<Property1> + MyClass<Property2> cannot be automatically generated by the compiler. This policy should be specified by the user. But how to design that in a clean way ?
Use type traits (as Kerrek SB pointed out in his comment).
template<typename T, typename U>
struct CommonPropertyTypeTraits : std::common_type<T,U>{}; //default version works if the types have a common type
std::common_type will only work if the type is implicitly convertable. If there is no common type which is implicitly convertable or if the user wants a different one they can specialize CommonPropertyTypeTraits:
template<>
struct CommonPropertyTypeTraits<MyType,MyOtherType> {
using type = SomeType;
}
The body of your function would then be:
template <class Property1, class Property2>
inline MyClass<typename CommonPropertyTypeTraits<Property1,Property2>::type> operator+(const MyClass<Property1> lhs, const MyClass<Property2> rhs)
{
return {lhs._data + rhs._data};
}
Note that this will give a pretty ugly error if there is not implicitly convertable common type and the user did not specialize the traits template. One could make a SFINAE test to make a better error:
template <class Property1, class Property2, typename=typename CommonPropertyTypeTraits<Property1,Property2>::type>
inline MyClass<typename CommonPropertyTypeTraits<Property1,Property2>::type> operator+(const MyClass<Property1> lhs, const MyClass<Property2> rhs)
{
return {lhs._data + rhs._data};
}
I'm still not quite certain what you ultimately want to use this for. If it is for dimensional analysis (i.e. tracking the types of different unit systems at compile time and making necessary conversions and issuing necessary errors) have a look at boost.Unit.

C++ STL fails to find comparator for nested class

I expected this code to work, but it fails to compile with GCC. It does compile if you lift the inner class out.
#include <algorithm>
template <typename T>
struct Outer
{
struct Inner
{
int x;
};
Inner vec[3];
};
template <typename T>
bool operator <(const typename Outer<T>::Inner& lhs, const typename Outer<T>::Inner& rhs)
{
return lhs.x < rhs.x;
}
int main()
{
Outer<int> out;
Outer<int>::Inner in;
std::lower_bound(out.vec, out.vec + 3, in);
}
GCC 4.4 has this to say:
...
bits/stl_algo.h:2442: error: no match for ‘operator<’ in ‘* __middle < __val’
GCC 4.7 prints a lot more stuff, including the above, ending with this:
...
bits/stl_algobase.h:957:4: note: couldn't deduce template parameter ‘T’
I'm willing to believe it's not well-formed C++, but why not?
The problem as you mentioned is that the compiler couldn't deduce template parameter T. That is because typename Outer::Inner is a nondeduced context context for T.
When a template parameter is used only in a nondeduced context, it's not taken into consideration for template argument deduction. The details are at section 14.8.2.4 of the C++ Standard (2003).
Why? If you specialize Outer as:
template <>
struct Outer<int>
{
struct Inner
{
double y;
};
Inner vec[3];
};
There is no way for the compiler to deduce if Outer::Inner should reference this definition or the previous one.
Now, onto solutions. There are a couple of possible resolutions:
Move the operator< inside Inner and make it a friend function.
Define operator< inside Inner like M M. suggested.
Suggested by Johannes Schaub - litb: Derive inner from a CRTP base parameterized by inner. Then make the parameter type the CRTP class and cast the parameter reference to the derived inner class, the type of which is given by the template argument you deduce.
I tried out the CRTP approach since it sounds so cool!:
template <typename Inner>
struct Base
{
};
template <typename T>
struct Outer
{
struct Inner : Base<Inner>
{
T x;
};
Inner vec[3];
};
template <typename T>
bool operator< (const Base<T>& lhs, const Base<T>& rhs)
{
return static_cast<const T&>(lhs).x < static_cast<const T&>(rhs).x;
}
Related answers: 1, 2, 3
Here's another workaround.
Why don't you use a custom comparer?
template <typename T>
struct Comparer
{
bool operator()(const typename Outer<T>::Inner& lhs, const typename Outer<T>::Inner& rhs)
{
return lhs.x < rhs.x;
}
};
int main()
{
Outer<int> out;
Outer<int>::Inner in;
std::lower_bound(out.vec, out.vec + 3, in, Comparer<int>());
}
Hope this works for you.
If you overload a specific operator< for int the problem will vanish:
bool operator<(const typename Outer<int>::Inner& lhs,
const typename Outer<int>::Inner& rhs)
{
return lhs.x < rhs.x;
}
The simpler solution is defining operator< inside Inner:
template<typename T>
struct Outer
{
struct Inner
{
int x;
bool operator<(const Inner& obj) const
{
return x < obj.x;
}
};
Inner vec[3];
};
Also, it's just a quick solution. And my answer is not the why compiler can not find operator< in nested situation in template mode.
Mar0ux's answer is pretty good. You can find additional information here:
Stephan T. Lavavej: Core C++, 2 of n
You should watch the whole video series - it contains a lot of useful information but you might start from minute 34 or so to get your question answered. Stephen mentions one basic rule to keep in mind:
:: is a brick wall for template argument deduction, i.e. a template argument T on the left side can't be deduced.

Does C++11 change the behavior of explicitly calling std::swap to ensure ADL-located swap's are found, like boost::swap?

Background
Consider the following code:
#include <utility>
namespace ns
{
struct foo
{
foo() : i(0) {}
int i;
private:
foo(const foo&); // not defined,
foo& operator=(const foo&); // non-copyable
};
void swap(foo& lhs, foo& rhs)
{
std::swap(lhs.i, rhs.i);
}
}
template <typename T>
void do_swap(T& lhs, T& rhs); // implementation to be determined
int main()
{
ns::foo a, b;
do_swap(a, b);
}
In C++03, this implementation of do_swap would be considered "broken":
template <typename T>
void do_swap(T& lhs, T& rhs)
{
std::swap(lhs, rhs);
}
By explicitly specifying std::, it prohibits ns::swap from being found via argument-dependent lookup. (It then fails to compile because std::swap tries to copy a foo, which is not allowed.) Instead, we do this:
template <typename T>
void do_swap(T& lhs, T& rhs)
{
using std::swap; // allow std::swap as a backup if ADL fails to find a swap
swap(lhs, rhs); // unqualified call to swap, allow ADL to operate
}
Now ns::swap is found and std::swap, being less specialized, is not used. It's uglier, but it works and is understandable in hind-sight. boost::swap wraps this up nicely for us (and provides array overloads):
#include <boost/swap.hpp>
template <typename T>
void do_swap(T& lhs, T& rhs)
{
boost::swap(lhs, rhs); // internally does what do_swap did above
}
Question
Does std::swap take on the behavior of boost::swap in C++11? If not, why?
To me it seems obvious that it ought to. Any code broken by the change was probably quite flimsy in the first place (algorithms and containers, like std::sort and std::vector, were underspecified; implementations were allowed to call ADL swap's or not indeterminately), so the change would be for the better. Additionally, std::swap is now defined for arrays, so change at all certainly isn't out of the question.
However, while §17.6.3.2 specifies that all calls to swap within the standard library must be done without std:: qualification (fixing the problem with algorithms and containers noted above), it fails to touch on std::swap itself. It even gives examples of swapping values that include using std::swap;. Likewise §20.2.2 (where std::swap is specified) doesn't say a word on ADL.
Lastly, GCC does not enable ADL in their std::swap implementation (nor does MSVC, but that's not saying much). So I must be wrong that std::swap takes on the behavior of boost::swap, but I don't understand why the change wasn't made. :( And I'm not alone!
I would have had to vote against your proof-of-concept implementation had it been proposed. I fear it would break the following code, which I'm pretty sure I've seen in the wild at least once or twice over the past dozen years.
namespace oops
{
struct foo
{
foo() : i(0) {}
int i;
void swap(foo& x) {std::swap(*this, x);}
};
void swap(foo& lhs, foo& rhs)
{
lhs.swap(rhs);
}
}
Whether you think the above is good code or bad, it works as the author intends in C++98/03 and so the bar for silently breaking it is pretty high. Telling users that in C++11 they would no longer have to write using std::swap; isn't a sufficiently high benefit to outweigh the disadvantage of silently turning the above code into infinite recursion.
Another way to get out of writing using std::swap; is to use std::iter_swap instead:
template <typename T>
void do_swap(T& lhs, T& rhs)
{
std::iter_swap(&lhs, &rhs); // internally does what do_swap did above
}
In C++20, this is finally standardized:
std::swap(a, b);
This uses ADL to call the correct overload and imposes the correct requirements to use in SFINAE. The magic is specified in [namespace.std]/7:
Other than in namespace std or in a namespace within namespace
std, a program may provide an overload for any library function
template designated as a customization point, provided that (a) the
overload's declaration depends on at least one user-defined type and
(b) the overload meets the standard library requirements for the
customization point.174 [ Note: This permits a
(qualified or unqualified) call to the customization point to invoke
the most appropriate overload for the given arguments. — end
note ]
174) Any library customization point must be prepared
to work adequately with any user-defined overload that meets the
minimum requirements of this document. Therefore an implementation may
elect, under the as-if rule ([intro.execution]), to provide any
customization point in the form of an instantiated function object
([function.objects]) even though the customization point's
specification is in the form of a function template. The template
parameters of each such function object and the function parameters
and return type of the object's operator() must match those of the
corresponding customization point's specification.
(emphasis mine)
And swap is designated as a customization point in [utility.swap]:
template<class T>
constexpr void swap(T& a, T& b) noexcept(see below);
Remarks: This function is a designated customization point ([namespace.std]) and shall not participate in overload resolution
unless is_­move_­constructible_­v<T> is true and
is_­move_­assignable_­v<T> is true. The expression inside
noexcept is equivalent to:
is_nothrow_move_constructible_v<T> && is_nothrow_move_assignable_v<T>
Requires: Type T shall be Cpp17MoveConstructible (Table 26) and Cpp17MoveAssignable (Table 28).
Effects: Exchanges values stored in two locations.
(emphasis mine)
Here's a proof-of-concept implementation:
#include <utility>
// exposition implementation
namespace std_
{
namespace detail
{
// actual fallback implementation
template <typename T>
void swap(T& lhs, T& rhs)
{
T temp = std::move(lhs);
lhs = std::move(rhs);
rhs = std::move(temp);
}
}
template <typename T>
void swap(T& lhs, T& rhs)
{
using detail::swap; // shadows std_::swap, stops recursion
swap(lhs, rhs); // unqualified call, allows ADL
}
}
namespace ns
{
struct foo
{
foo() : i(0) {}
int i;
private:
foo(const foo&); // not defined,
foo& operator=(const foo&); // non-copyable
};
void swap(foo& lhs, foo& rhs)
{
std::swap(lhs.i, rhs.i);
}
}
int main()
{
int i = 0, j = 0;
std_::swap(i, j);
ns::foo a, b;
std_::swap(a, b);
}
Well, boost::swap() dispatches to std::swap(). To have std::swap() do something similar to boost::swap() it would need to delegate somewhere else. What is this somewhere else? The standard doesn't mandate another version of swap() which provides the actual implementation. This can be done but the standard doesn't mandate it.
Why it doesn't do it? I didn't see any proposal proposing this implementation. If someone had wanted this to do done I'm sure it would have been proposed.