What's the canonical way of overloading the compound assignment operator? - c++

Seems kind of a tough problem because you can't add new member functions to vector. This form avoids the least amount of copies:
std::vector<T>& operator+=(std::vector<T>& lhs, const std::vector<T>& rhs)
But fails for self-assignment, so the only one that works for self-assignment is:
template <typename T>
std::vector<T>& operator+=(std::vector<T>& lhs, std::vector<T> rhs)
{
lhs.insert(lhs.end(), rhs.begin(), rhs.end());
return lhs;
}
But this requires an extra copy. What's the correct way of doing this?
It was ambiguous in my question that the above forms "don't work" because they appear to work for ints (although not for std::strings). It was pointed out this is because it's undefined behavior.

The problem with:
template <typename T>
std::vector<T>& operator+=(std::vector<T>& lhs, const std::vector<T>& rhs)
{
lhs.insert(lhs.end(), rhs.begin(), rhs.end());
return lhs;
}
is not the signature, it's passing iterators to insert that become invalid before insert has completed.
Just use the correct technique for appending a vector to itself, and no extra copy is needed.
template <typename T>
void concat_in_place(std::vector<T>& lhs, const std::vector<T>& rhs)
{
auto left_count = lhs.size();
auto right_count = rhs.size();
lhs.resize(left_count + right_count);
std::copy_n(rhs.begin(), right_count, lhs.begin() + left_count);
}

You should not, as a matter of policy, overload two different std types with an operator between them. It may be undefined behavior: the standard is ambiguous.
If you want operator syntax on std containers, I would recommend named operators. It is also more clear, as operators on a vector could be container operators or element-wise operators, which is why they are missing by default. v +append= v2; is clearly appending. (create a static append object, and overload its lhs and rhs operators with your vector, and use expression templates in the intermediate steps)
// mini named operator library. Only supports + for now:
template<class Kind>
struct named_operator {};
template<class OP, class LHS> struct plus_ {
LHS lhs;
template<class RHS>
decltype(auto) operator=(RHS&&rhs)&&{
return plus_assign(std::forward<LHS>(lhs), OP{}, std::forward<RHS>(rhs));
}
template<class RHS>
decltype(auto) operator+(RHS&&rhs)&&{
return plus(std::forward<LHS>(lhs), OP{}, std::forward<RHS>(rhs));
}
};
template<class Tag, class LHS>
plus_<Tag,LHS> operator+( LHS&& lhs, named_operator<Tag> ) {
return {std::forward<LHS>(lhs)};
}
// creating a named operator:
static struct append_tag:named_operator<append_tag> {} append;
// helper function, finds size of containers and arrays:
template<class T,std::size_t N>
constexpr std::size_t size( T(&)[N] ) { return N; }
template<class C>
constexpr auto size(C&& c)->decltype(c.size()) { return c.size(); }
// implement the vector +append= range:
template<class T, class A, class RHS>
std::vector<T,A>& plus_assign(std::vector<T,A>&lhs, append_tag, RHS&& rhs) {
auto rhs_size = size(rhs);
lhs.reserve(lhs.size()+rhs_size);
using std::begin; using std::end;
copy_n( begin(rhs), rhs_size, back_inserter(lhs) );
return lhs;
}
// implement container +append+ range:
template<class LHS, class RHS>
LHS plus( LHS lhs, append_tag, RHS&& rhs ) {
using std::begin; using std::end; using std::back_inserter;
copy_n( begin(rhs), size(rhs), back_inserter(lhs) );
return std::move(lhs);
}
live example
Note that std::vector<int> +append= std::list<int> +append+ std::array<double, 3> works with the above code.

Related

A "catch-all" for SFINAE functions?

I am trying to make a typed compare function that do some customized comparison for different types.
#include <type_traits>
template <typename T>
bool typedCompare(const T& lhs, const T& rhs)
{
return lhs == rhs; // default case, use ==
}
template <typename T>
typename std::enable_if<std::is_floating_point<T>::value, bool>::type
typedCompare(const T& lhs, const T& rhs)
{
return (lhs - rhs) < 1e-10;
}
int main()
{
typedCompare(1, 1);
typedCompare(1.0, 1.0);
return 0;
}
Here I have a special version for double that compare the difference with a small amount (please ignore the fact that I did not use std::abs()). I have a few other custom types that I need to do some special comparison, and I cannot change their == operator for some reason.
Besides, I still want to have a "catch-all" style function that employs the == operator. My problem is that when trying to compile this code snippet the compiler complains about that typedCompare(1.0, 1.0) is ambiguous, it can choose either of the two functions provided.
Why? And how could I resolve this issue?
Thanks.
Why?
In short, you use SFINAE incorrectly so both function templates are valid when you call typedCompare for doubles.
And how could I resolve this issue?
In this particular case, fix SFINAE to make it work correctly:
template <typename T>
typename std::enable_if<!std::is_floating_point<T>::value, bool>::type
typedCompare(const T& lhs, const T& rhs)
{
return lhs == rhs; // default case, use ==
}
template <typename T>
typename std::enable_if<std::is_floating_point<T>::value, bool>::type
typedCompare(const T& lhs, const T& rhs)
{
return (lhs - rhs) < 1e-10;
}
Please note, that this solution is not that good in terms of customization for many types. Another way is to use tag dispatching:
struct floating_point_tag {};
// other tags
template <typename T>
bool typedCompare(const T& lhs, const T& rhs, floating_point_tag) {
return (lhs - rhs) < 1e-10;
}
// implementations for other tags
template <typename T>
bool typedCompare(const T& lhs, const T& rhs) {
if (std::is_floating_point<T>::value) {
return typedCompare(lhs, rhs, floating_point_tag{});
}
// other checks here
return lhs == rhs;
}
Finally, with C++17 you might make use of if constexpr:
template <typename T>
bool typedCompare(const T& lhs, const T& rhs) {
if constexpr (std::is_floating_point<T>::value) {
return (lhs - rhs) < 1e-10;
} else { // add other if-else here
return lhs == rhs;
}
}
The problem with the code in your question is that when the floating point typedCompare() is SFINAE enabled, collide with the general version because the compiler can't prefer one version over the other.
To solve this problem, I suggest you another way, based on template partial specialization (available only with structs and classes, so need a helper struct)
If you define an helper struct as follows
template <typename T, typename = void>
struct typedCompareHelper
{
static constexpr bool func (T const & lhs, T const & rhs)
{ return lhs == rhs; } // default case, use ==
};
template <typename T>
struct typedCompareHelper<T,
typename std::enable_if<std::is_floating_point<T>::value>::type>
{
static constexpr bool func (T const & lhs, T const & rhs)
{ return (lhs - rhs) < 1e-10; }
};
you avoid the ambiguity problem because the typedCompareHelper specialization in more specialized of the generic one.
You can simply add more specializations for different special cases making only attention in avoiding collisions (different specializations of the same level that apply over the same type).
Your typedCompare() become simply
template <typename T>
bool typedCompare (T const & lhs, T const & rhs)
{ return typedCompareHelper<T>::func(lhs, rhs); }
I like both the solution from #max66 with a helper template and the multiple solutions from #Edgar Rokyan.
Here is another approach that can be used for what you want using a helper template function.
#include <type_traits>
#include <iostream>
#include <string>
// elipsis version is at the bottom of the overload resolution priority.
// it will only be used if nothing else matches the overload.
void typeCompare_specialized(...)
{
std::cout << "SHOULD NEVER BE CALLED!!!\n";
}
template <typename T>
typename std::enable_if<std::is_floating_point<T>::value, bool>::type
typeCompare_specialized(const T& lhs, const T& rhs)
{
std::cout << "floating-point version\n";
return (lhs - rhs) < 1e-10;
}
template <typename T>
typename std::enable_if<std::is_integral<T>::value, bool>::type
typeCompare_specialized(const T& lhs, const T& rhs)
{
std::cout << "integral version\n";
return lhs == rhs;
}
template <typename T>
auto typedCompare(const T& lhs, const T& rhs)
-> typename std::enable_if<std::is_same<bool,decltype(typeCompare_specialized(lhs, rhs))>::value,bool>::type
{
return typeCompare_specialized(lhs, rhs);
}
template <typename T>
auto typedCompare(const T& lhs, const T& rhs)
-> typename std::enable_if<!std::is_same<bool,decltype(typeCompare_specialized(lhs, rhs))>::value,bool>::type
{
std::cout << "catch-all version\n";
return lhs == rhs;
}
int main()
{
typedCompare(1, 1);
typedCompare(1.0, 1.0);
typedCompare(std::string("hello"), std::string("there"));
return 0;
}
Running the above program will yield the following output:
integral version
floating-point version
catch-all version
Again, I would prefer to use one of the previous answers mentioned. I include this possibility for completeness.
I would also like to add that you should make sure your typeCompare_specialized() template versions should not have any overlap, otherwise you could get a compiler error declaring that there are multiple candidate overloads to use.

Should I extend std::less for a comparison functor?

I want to create a shared_ptr content-comparison functor to stand in for std::less<T> in associative containers and std algorithms. I've seen several examples of custom comparators that use the following (or similar) model:
template <typename T>
struct SharedPtrContentsLess {
bool operator()(const boost::shared_ptr<T>& lhs,
const boost::shared_ptr<T> rhs) const {
return std::less<T>(*lhs, *rhs);
//or: return (*lhs) < (*rhs);
}
//defining these here instead of using std::binary_functor (C++11 deprecated)
typedef boost::shared_ptr<T> first_argument_type;
typedef boost::shared_ptr<T> second_argument_type;
typedef bool return_type;
};
But why wouldn't I want to instead extend std::less? Like so:
template <typename T>
struct SharedPtrContentsLess : public std::less< boost:shared_ptr<T> > {
bool operator()(const boost::shared_ptr<T>& lhs,
const boost::shared_ptr<T> rhs) const {
return std::less<T>(*lhs, *rhs);
}
};
Does this buy me anything at all?
I would think this gets me the typedefs for free, as though I was extending the deprecated std::binary_function. In C++03, I actually would be extending it through std::less. However, this would also be portable from C++03 to C++11/14 and even C++17 when std::binary_function will be removed, as it just follows the changes in std::less.
I've read a bunch of answers on StackOverflow regarding std::less use, custom comparison functors, and even some of the Standard specs and proposals. I see specializations of std::less and guidance not to extend STL containers, but I can't seem to find any examples of extending std::less or guidance against it. Am I missing an obvious reason not to do this?
EDIT: Removed C++11 tag, as it is causing confusion to the answerers. I am hoping to get forward-portability, but C++03 is required. If you provide a C++11-only answer for others to use (totally fine), please note that.
You can create a reusable template towards any dereferencable object (i.e. any (smart) pointer) by simply forwarding the call to std::less or any other comparable object.
// c++11
template<template<class> Op, class T> struct deref_mixin;
template<template<class> Op, class T>
struct deref_mixin {
auto operator()(const T &l, const T &r) const
-> decltype(std::declval<Op<T>>()(*l, *r)) {
return Op<T>{}(*l, *r);
}
};
template<template<class> Op>
struct deref_mixin<Op, void> {
template<class T, class U>
auto operator()(const T &l, const U &r) const
-> decltype(std::declval<Op<T>>()(*l, *r)) {
return Op<void>{}(*l, *r);
}
};
template<class T> using less_deref = deref_mixin<std::less, T>;
template<class T> using greater_deref = deref_mixin<std::greater, T>;
template<class T> using my_comparator_deref = deref_mixin<my_comparator, T>;
// c++03
template<template<class> Op, class T>
struct deref_mixin {
bool operator()(const T &l, const T &r) const {
Op<T> op;
return op(*l, *r);
}
};
// Technically, the void template partial specialization isn't defined in c++03, but it should have been :)
template<template<class> Op>
struct deref_mixin<Op, void> {
template<class T, class U>
bool operator()(const T &l, const U &r) const {
Op<void> op;
return op(*l, *r);
}
};
template<class T> struct less_deref : deref_mixin<std::less, T> {};
As you said in your question if you inherit from std::less the you would get the three typedefs that are in std::less. What I like most about inheriting from it is it describes your intent. When I see
struct some_non_specific_name : std::less<some_type>
I know right there that this is a functor that is going to behave as an < for some_type. I don't have to read the struct body to find out anything.
As far as I can see, you are not missing any disadvantage. As you mentioned, you would automatically get the typedefs. The operator< has to be defined in both cases, and there is no difference in its implementation.
There is one thing that you might get that you might find neat, bad or just not applicable to your usecase (from here and here): there is a specialization of std::less for std::less<void> that has a template operator< deduces the return type of the operator< for the given arguments.
Unless you intend to use a SharedPtrContentsLess<void> (which probably doesn't make sense at all), both solutions would be equivalent.
I'd write a deref_less. First, my_less that smartly calls std::less:
struct my_less {
template<class Lhs, class Rhs,
class R = std::result_of_t< std::less<>( Lhs const&, Rhs const& ) >
// class R = decltype( std::declval<Lhs const&>() < std::declval<Rhs const&>() )
>
R operator()(Lhs const&lhs, Rhs const&rhs)const{
return std::less<>{}(lhs, rhs); // or lhs<rhs
}
// exact same type uses `std::less<T>`:
template<class T,
class R = std::result_of_t< std::less<>( T const&, T const& ) >
>
R operator()(T const& lhs, T const& rhs)const{
return std::less<T>{}(lhs, rhs);
}
template<class Lhs, class Rhs,
std::enable_if_t< std::is_base_of<Lhs, Rhs>{} && !std::is_same<Lhs, Rhs>{} >* = nullptr
>
bool operator()(Lhs const* lhs, Rhs const* rhs)const{
return std::less<Lhs const*>{}(lhs, rhs);
}
template<class Lhs, class Rhs,
std::enable_if_t< std::is_base_of<Rhs, Lhs>{} && !std::is_same<Lhs, Rhs>{} >* = nullptr
>
bool operator()(Lhs const* lhs, Rhs const* rhs)const{
return std::less<Rhs const*>{}(lhs, rhs);
}
template<class Lhs, class Rhs,
std::enable_if_t<
!std::is_base_of<Rhs, Lhs>{}
&& !std::is_base_of<Lhs, Rhs>{}
&& !std::is_same<Lhs, Rhs>{}
>* = nullptr
>
bool operator()(Lhs const* lhs, Rhs const* rhs)const = delete;
};
then, a deref_less that does a * then calls myless:
struct deref_less {
template<class Lhs, class Rhs,
class R = std::result_of_t< my_less( decltype(*std::declval<Lhs>()), decltype(*std::declval<Rhs>()) ) >
>
R operator()(Lhs const& lhs, Rhs const&rhs)const {
return my_less{}( *lhs, *rhs );
}
};
in C++14, but everything I used is easy to replace (std::less<> can be replaced with decltype and <s for example).
Because std::less lacks a virtual destructor (i.e. implicit destructor only), inheriting from it could technically lead to undefined behavior. Since neither type contains any data members, destruction should work no matter how the object is referenced, but the standard forbids polymorphic deletion through static destructors because it presents a strong possibility of problems (slicing, incomplete deletion) in most cases.
See this answer:
Thou shalt not inherit from std::vector

C++ default parameter in template not allowed?

In my C++ code, I wrote like this:
template <typename T, typename Pred>
inline const T BestOfTwo(const T& lhs, const T& rhs, Pred p = std::less<T>())
{
return p(lhs, rhs) ? lhs : rhs;
}
But this didn't work when I called BestOfTwo(3, 5). The compiler told me that no instance of overload matched. So now I have to write it like this:
template <typename T, typename Pred = std::less<T> >
inline const T BestOfTwo(const T& lhs, const T& rhs, Pred p = Pred())
{
return p(lhs, rhs) ? lhs : rhs;
}
And this worked with no error when I called BestOfTwo(3, 5). But I think the previous style is more convenient and I didn't figure out where it went wrong. What are some suggestions?
Only the second version is correct (if you don't want to specify the Pred parameter manually), but only since C++11. There is already an answer from Angew that clarify why the first version is incorrect, without specification of the Pred parameter.
If you cannot use C++11 you should write two overloads (one with Pred and one without, that use std::less), since default template parameters for function templates are explicitly forbidden in C++98.
template<typename T, typename Pred>
inline const T BestOfTwo(const T& lhs, const T& rhs, Pred p = Pred())
{
//
}
template<typename T>
inline const T BestOfTwo(const T& lhs, const T& rhs)
{
return BestOfTwo<T, std::less<T> >(lhs, rhs);
}
The first version would work if you specified the template arguments explicitly:
BestOfTwo<int, std::less<int>>(3, 5)
The reason is that default function arguments cannot be used to deduce the type for a template parameter.

My vector is sorted and yet I'm getting a "sequence not ordered" error

Are there situations in which std::sort fails?
I've got a std::vector<KeyValPair<T>> queue with which I do the following
std::sort(queue.begin(), queue.end());
std::pair<iterator, iterator> match =
std::equal_range(queue.begin(), queue.end(), cost);
Exactly that. And then sometimes, not always, I get a "sequence not ordered" error.
The documentation describes sort and equal_range as using the same comparison functions, so I am confused how the vector could become unordered.
The vector type is the following class with custom comparison operators.
template<typename T>
class KeyValPair: public std::pair<double, T>
{
public:
KeyValPair(double d, T t): std::pair<double, T>(d, t){};
bool operator<(const KeyValPair<T>& rhs) const
{
return first < rhs.first;
}
bool operator==(const KeyValPair<T>& rhs) const
{
return second == rhs.second;
}
};
template<typename T>
bool operator< (const KeyValPair<T>& lhs, const double& rhs) {return lhs.first < rhs;};
template<typename T>
bool operator< (const double& lhs, const KeyValPair<T>& rhs) {return lhs < rhs.first;};
Could the comparison function be failing somehow? What else can cause this error?
As first psychically detected by #ecatmur, your problem is you are using < on doubles, and one or more of your doubles is a NaN.
A safe double ordering follows:
struct safe_double_order {
bool operator()(double lhs, double rhs) const {
if ((lhs != lhs) || (rhs != rhs)) // NaN detector
return (lhs!=lhs)>(rhs!=rhs); // order NaN less than everything, including -infinity
return lhs < rhs;
}
};
Next, we can write a key-sorter:
template<class K, class O=std::less<K>>
struct key_sorter {
struct helper {
K const& k;
helper( K const& o ):k(o) {}
template<typename V>
helper( std::pair<K, V> const& o ):k(o.first) {}
bool operator<( helper const& o ) const {
return O{}( k, k.o );
}
};
bool operator()( helper lhs, helper rhs ) const {
return lhs < rhs;
}
};
which passed a key-type and an optional ordering functor lets you search/sort std::pair<Key,?> with Key types directly.
std::vector< std::pair<double, X> > vec;
std::sort( vec.begin(), vec.end(), key_sorter<double, safe_double_order>{} );
auto match = std::equal_range( vec.begin(), vec.end(), value, key_sorter<double, safe_double_order>{} );
There are some C++11isms above, but the general design should be clear if you are using C++03.

Operator Overload for Template Class Auto Type Conversion

I have a template class for which I'm overloading operator+, but need it to potentially return a different data type than what the class is instantiated for. For example, the following snippet performs the standard mathematical definition of either vector*vector (inner product), vector*scalar, or scalar*vector. To be specific, let's say I have a Vector<int> and the scalar is of type double -- I want to return a double from the "vector*scalar" operator+ function.
I'm pretty sure I'm missing some template<class T> statements for the friend functions(?), but this snippet isn't meant to compile.
template<class T>
class Vector
{
private:
std::vector<T> base;
public:
friend Vector operator*(const Vector& lhs, const Vector& rhs); // inner product
Vector<T> operator*(const T scalar); // vector*scalar
friend Vector operator*(const T scalar, const Vector& rhs); // scalar*vector
};
template<class T>
Vector<T> operator*(const Vector<T>& lhs, const Vector<T>& rhs) // inner product
{
assert( lhs.base.size() == rhs.base.size() );
Vector result;
result.base.reserve(lhs.base.size());
std::transform( lhs.base.begin(), lhs.base.end(), rhs.base.begin(), std::back_inserter(result.base), std::multiplies<T>() );
return result;
}
template<class T>
Vector<T> Vector<T>::operator*(const T scalar) // vector*scalar
{
Vector result;
result.base.reserve(base.size());
std::transform( base.begin(), base.end(), std::back_inserter(result.base), std::bind1st(std::multiplies<T>(), scalar) );
return result;
}
template<class T>
Vector<T> operator*(const T scalar, const Vector<T>& rhs) // scalar*vector
{
Vector result;
result.base.reserve(rhs.base.size());
std::transform( rhs.base.begin(), rhs.base.end(), std::back_inserter(result.base), std::bind1st(std::multiplies<T>(), scalar) );
return result;
}
I guess what you want is to return a Vector with the value type being the type returned by the per-component operation, which is not necessarily the type of the scalar.
For example:
Vector<int> * int -> Vector<int>
Vector<int> * double -> Vector<double>
Vector<double> * int -> Vector<double>
Vector<char> * float -> Vector<float>
etc.
For this, you should define the two input types separately, let's say T1 and T2 (and one or two of the operands is a Vector of it). You don't want to simply use the scalar type (for vector * scalar, or scalar * vector operation) as the result, otherwise it might be converted (see my 3rd example: The result would then be Vector<int>.
The above can be done using decltype to find the third (result) type.
For simplicity, define the operator as a non-member:
template<typename T1, typename T2, typename T3 = decltype(std::declval<T1>() * std::declval<T2>())>
Vector<T3> operator*(const Vector<T1>& lhs, const T2 & scalar) // inner product
{
Vector<T3> result;
//...
return result;
}
The interesting part is
T3 = decltype(std::declval<T1>() * std::declval<T2>())
Here, we find the type T3 using the other two types T1 and T2. First, we construct two values with unimportant value (the std::declval function is a helper function returning the type given as a template parameter). Then we multiply those values, but again the result is unimportant; we're only interested in the type. That's what the third part does: decltype gives you the type of the expression (without evaluating it).
The other operators can be implemented analogous.
In order to make those operators friends, you need the syntax
template<...> friend ...
as seen in Danvil's answer.
The friend declaration should look like this:
template<class S>
class Vector
{
private:
std::vector<S> base;
public:
template<class T> friend T operator*(const Vector<T>& lhs, const Vector<T>& rhs); // inner product
template<class T> friend Vector<T> operator*(const T scalar); // vector*scalar
template<class T> friend Vector<T> operator*(const T scalar, const Vector<T>& rhs); // scalar*vector
};
And the rest of the code like this:
template<class T>
T operator*(const Vector<T>& lhs, const Vector<T>& rhs) // inner product
{
assert( lhs.base.size() == rhs.base.size() );
Vector<T> result;
result.base.reserve(lhs.base.size());
std::transform( lhs.base.begin(), lhs.base.end(), rhs.base.begin(), std::back_inserter(result.base), std::multiplies<T>() );
return result;
}
template<class T>
Vector<T> operator*(const Vector<T>& lhs, const T scalar) // vector*scalar
{
return scalar*lhs;
}
template<class T>
Vector<T> operator*(const T scalar, const Vector<T>& rhs) // scalar*vector
{
Vector<T> result;
result.base.reserve(rhs.base.size());
std::transform( rhs.base.begin(), rhs.base.end(), std::back_inserter(result.base), std::bind1st(std::multiplies<T>(), scalar) );
return result;
}
You can add more template parameters as such:
template<class T, class S>
Vector<S> Vector<T>::operator*(const S scalar)
and make sure you use S for the scalar type and T for the vector element type in the correct places in the function body.