Operator overloading, Rvalues, C++ - c++

Consider a struct
template<typename T, size_t N>
struct Something {
std::array<T,N> args;
// Some constructors
};
Now let's overload = operator for Something<T>. In fact I can implement it two ways.
First Way
Something& operator=(const Something& rhs){
// an implementation with copy semantics
}
Something& operator=(Something&& rhs) {
// an implementation with move semantics
}
Second Way
Something& operator=(Something rhs){
// implement with move semantics
}
So, my question is what is the most standard way and the most optimal way to do overloading First Way or Second Way?

For this particular case you should not implement the assignment operator. The compiler does that already for you:
#include <array>
#include <cstddef>
template<typename T, size_t N>
struct Something {
std::array<T,N> args;
};
int main() {
Something<int,42> a;
Something<int,42> b;
a = b;
}
Demo
For the general case I refer you to What are the basic rules and idioms for operator overloading?. And consider that not everything can be moved and not everything can be copied. Moreover, sometimes a move is just a copy. Hence, it depends.

Related

Hashable type with overwritten operators or external functors

To use a custom type in a std::unordered_set I have to options.
1) Implement the == operator for my type and specialize std::hash
struct MyType {
int x;
bool operator==(const MyType& o) {
return this.x == o.x;
}
};
namespace std
{
template<>
struct hash<MyType> {
size_t operator()(const MyType& o) const {
return hash<int>()(o.x);
}
};
}
std::unordered_set<MyType> mySet;
Or 2), provide functor classes:
struct MyTypeHash {
size_t operator()(const MyType& o) const {
return std::hash<int>()(o.x);
}
};
struct MyTypeCompare {
bool operator()(const MyType& o1, const MyType& o2) const {
return o1.x == o2.x;
}
};
std::unordered_set<MyType, MyTypeHash, MyTypeCompare> mySet;
The second approach lets me choose new behaviour for every new instantion of std::unordered_set, while with the first approach the behaviour as being part of the type itself will always be the same.
Now, if I know that I only ever want a single behaviour (I'll never define two different comparators for MyType), which approach is to be preferred? What other differences exist between those two?
Attaching the behavior to the type allows for code like
template<template<class> Set,class T>
auto organizeWithSet(…);
/* elsewhere */ {
organizeWithSet<std::unordered_set,MyType>(…);
organizeWithSet<std::set,MyType>(…);
}
which obviously cannot pass custom function objects.
That said, it is possible to define
template<class T>
using MyUnorderedSet=std::unordered_set<T, MyTypeHash,MyTypeCompare>;
and use that as a template template argument, although that introduces yet another name and might be considered less readable.
Otherwise, you have to consider that your operator== is simultaneously the default for std::unordered_set and std::find, among others; if the equivalence you want for these purposes varies, you probably want named comparators. On the other hand, if one suffices, C++20 might even let you define it merely with =default.

swap overload that can take l-value and r-value references and different types

I want to write an overload of swap that I expect the compiler to find through ADL. This version of swap can swap two arguments of different types, with the types being defined in the same namespace. This is what I thought might work:
#include <utility>
namespace mynamespace
{
struct FirstStruct
{
};
struct SecondStruct
{
};
template<typename first_t, typename second_t>
void swap(first_t&&, second_t&&)
{
...
}
}
I use a universal reference because I want it to work with both r-value refs (temporaries) as well as l-value refs.
However, this, and many other variants I've tried in an attempt to avoid having to write a multitude of specialized overloads, doesn't seem to work, especially when a using std::swap is specified before making an unqualified call to swap, which is what I expect most users would do.
Do I have no other option but to write 4 x 4 = 16 variants of swap?
You just need 3 overloads:
template<typename first_t, typename second_t>
void swap(first_t&&, second_t&&)
{
//...
}
// for better match than std::swap
void swap(FirstStruct& lhs, FirstStruct& rhs) { swap<FirstStruct&, FirstStruct&>(lhs, rhs); }
void swap(SecondStruct& lhs, SecondStruct& rhs) { swap<SecondStruct&, SecondStruct&>(lhs, rhs); }
Demo

SFINAE with boost enable if

I am trying to implement a templated pair class that uses sfinae to distinguish between array and non-array types. So far, I have the following code:
template <typename T>
class pair
{
public:
//default constructors. one for non-array types and one for arrays. These work.
template <typename temp_type = T>
pair(typename boost::disable_if<boost::is_array<temp_type>>::type* ignore = 0)
: first(T())
, second(T())
{}
template <typename temp_type = T>
pair(typename boost::enable_if<boost::is_array<temp_type>>::type* ignore = 0)
{}
//assignment operator attempts for non-array types (not in code at same time.) These don't work.
template<typename temp_type = T>
pair<temp_type>& operator=(pair<typename boost::disable_if<boost::is_array<temp_type>>::type> const& rhs)
{
this->first = rhs.first;
this->second = rhs.second;
return *this;
}
template<typename temp_type = T>
auto operator=(pair<temp_type> const& rhs) -> pair<typename boost::disable_if<boost::is_array<temp_type>>::type>&
{
this->first = rhs.first;
this->second = rhs.second;
return *this;
}
T first;
T second;
};
The first attempt at the assignment operator fails with an "illegal use of type void" error. The second compiles, but when I debug, MSVC tells me that "no executable code is associated with that line." I am using the MSVC 12 64 bit compiler.
If you could offer any insight as to what is wrong here, that would be very helpful.
Also, just a few observations I've made about sfinae in c++ (that may be right or wrong):
sfinae requires the member function to be templated but it cannot use the class template type(s) for this purpose. Why?
sfinae can be done by only modifying the template parameters and not the return type or inputs. How, exactly does this work. What's the flexibility of this method?
I know this is long winded and references topics covered in numerous other posts, but I haven't been able to put those explanations together in a way that concisely explains sfinae in c++.
Some posts I've read:
Explain C++ SFINAE to a non-C++ programmer (high level intro)
Select class constructor using enable_if (sfinae for constructors)
Thanks for any help.
EDIT:
I have modified my code based on the comments below and I still can't get this to work as expected (The compiler is not even seeing the source.) It's an interesting situation because the example is completely contrived and the default assignment operator actually works in this scenario. Nevertheless, I don't think the compiler should be overriding my attempt to overload the operator.
I have tried the following 4 methods and none of them seem to be built into the executable:
template<typename temp_type = T>
pair<temp_type>& operator=(pair<typename boost::disable_if<boost::is_array<temp_type>, temp_type>::type> const& rhs)
{
this->first = rhs.first;
this->second = rhs.second;
return *this;
}
template<typename temp_type = T>
auto operator=(pair<temp_type> const& rhs) -> pair<typename boost::disable_if<boost::is_array<temp_type>, temp_type>::type>&
{
this->first = rhs.first;
this->second = rhs.second;
return *this;
}
template<typename ret_type = boost::disable_if<boost::is_array<T>, T>::type, typename = void>
pair<ret_type>& operator=(pair<ret_type> const& rhs)
{
this->first = rhs.first;
this->second = rhs.second;
return *this;
}
template<typename temp_type = T, typename boost::enable_if<boost::is_array<temp_type>, temp_type>::type = 0>
pair<T>& operator=(pair<temp_type> const& rhs)
{
this->first = rhs.first;
this->second = rhs.second;
return *this;
}
Thoughts?
Don't forget, converting assignment can be implemented by template functions, but copy and move assignment operators can't.
A user-declared copy assignment operator X::operator= is a non-static non-template member function of class X with exactly one parameter of type X, X&, const X&, volatile X& or const volatile X&.
If the class definition does not explicitly declare a copy assignment operator, one is declared implicitly. If the class definition declares a move constructor or move assignment operator, the implicitly declared copy assignment operator is defined as deleted; otherwise, it is defined as defaulted
A user-declared move assignment operator X::operator= is a non-static non-template member function of class X with exactly one parameter of type X&&, const X&&, volatile X&&, or const volatile X&&.
and the note
Because a template assignment operator or an assignment operator taking an rvalue reference parameter is never a copy assignment operator, the presence of such an assignment operator does not suppress the implicit declaration of a copy assignment operator. Such assignment operators participate in overload resolution with other assignment operators, including copy assignment operators, and, if selected, will be used to assign an object.
(above quotes found in section 12.8, wording from draft n3936)
Why your attempts fail to work is explained in #BenVoigt 's answer, here is only an alternative solution. You can dispatch the operator= call to an appropriate overload depending on the type your template was instantiated with:
#include <iostream>
#include <boost/type_traits.hpp>
template <typename T>
class pair
{
public:
pair& operator=(pair const& rhs)
{
return assign(rhs, boost::is_array<T>());
}
private:
pair& assign(pair const&, boost::true_type)
{
std::cout << "array" << std::endl;
return *this;
}
pair& assign(pair const&, boost::false_type)
{
std::cout << "not array" << std::endl;
return *this;
}
};
int main()
{
pair<int> a, b;
b = a;
pair<int[]> c, d;
c = d;
}
Output:
not array
array
And you can do the same with constructors, delegating (C++11) the call to another one:
pair() : pair(boost::is_array<T>()) {}
pair(boost::true_type) { /*initialize array pair*/ }
pair(boost::false_type) { /*initialize non-array pair*/ }
Code looks cleaner and you don't have to compete with the compiler on whose operator= better matches the actual argument.
DEMO

is there any reason to use non noexcept Move constructor [duplicate]

To my understanding, move-constructors and move-assign must be marked noexcept in order for the compiler to utilize them when, for example, reallocating inside a vector.
However, is there any real-world case where a move-assign, move-construct might actually throw?
Update:
Classes that for example has an allocated resource when constructed cant be no-throw move.
However, is there any real-world case where a move-assign,
move-construct (or swap) might actually throw?
Yes. Consider an implementation of std::list. The end iterator must point "one past the last element" in the list. There exist implementations of std::list where what end points to is a dynamically allocated node. Even the default constructor allocates such a node so that when you call end(), there is something to point to.
In such an implementation, every constructor must allocate a node for end() to point to… even the move constructor. That allocation may fail, and throw an exception.
This same behavior can extend to any node-based container.
There are also implementations of these node-based containers that do a "short-string" optimization: They embed the end node within the container class itself, instead of dynamically allocating. Thus the default constructor (and move constructor) need not allocate anything.
The move assignment operator can throw for any container<X> if for the container's allocator propagate_on_container_move_assignment::value is false, and if the allocator in the lhs is not equal to the allocator in the rhs. In that case the move assignment operator is forbidden from transferring memory ownership from the rhs to the lhs. This can not happen if you are using std::allocator, as all instances of std::allocator are equal to one another.
Update
Here is a conforming and portable example of the case when propagate_on_container_move_assignment::value is false. It has been tested against the latest version of VS, gcc and clang.
#include <cassert>
#include <cstddef>
#include <iostream>
#include <vector>
template <class T>
class allocator
{
int id_;
public:
using value_type = T;
allocator(int id) noexcept : id_(id) {}
template <class U> allocator(allocator<U> const& u) noexcept : id_(u.id_) {}
value_type*
allocate(std::size_t n)
{
return static_cast<value_type*>(::operator new (n*sizeof(value_type)));
}
void
deallocate(value_type* p, std::size_t) noexcept
{
::operator delete(p);
}
template <class U, class V>
friend
bool
operator==(allocator<U> const& x, allocator<V> const& y) noexcept
{
return x.id_ == y.id_;
}
};
template <class T, class U>
bool
operator!=(allocator<T> const& x, allocator<U> const& y) noexcept
{
return !(x == y);
}
template <class T> using vector = std::vector<T, allocator<T>>;
struct A
{
static bool time_to_throw;
A() = default;
A(const A&) {if (time_to_throw) throw 1;}
A& operator=(const A&) {if (time_to_throw) throw 1; return *this;}
};
bool A::time_to_throw = false;
int
main()
{
vector<A> v1(5, A{}, allocator<A>{1});
vector<A> v2(allocator<A>{2});
v2 = std::move(v1);
try
{
A::time_to_throw = true;
v1 = std::move(v2);
assert(false);
}
catch (int i)
{
std::cout << i << '\n';
}
}
This program outputs:
1
which indicates that the vector<T, A> move assignment operator is copy/moving its elements when propagate_on_container_move_assignment::value is false and the two allocators in question do not compare equal. If any of those copies/moves throws, then the container move assignment throws.
Yes, throwing move constructors exist in the wild. Consider std::pair<T, U> where T is noexcept-movable, and U is only copyable (assume that copies can throw). Then you have a useful std::pair<T, U> move constructor which may throw.
There is a std::move_if_noexcept utility in the standard library if you need (useful to implement std::vector::resize with at least the basic exception guarantee).
See also Move constructors and the Strong Exception Guarantee
Move constructors on classes with const data members can also throw. Check here for more info.

Move which throws?

To my understanding, move-constructors and move-assign must be marked noexcept in order for the compiler to utilize them when, for example, reallocating inside a vector.
However, is there any real-world case where a move-assign, move-construct might actually throw?
Update:
Classes that for example has an allocated resource when constructed cant be no-throw move.
However, is there any real-world case where a move-assign,
move-construct (or swap) might actually throw?
Yes. Consider an implementation of std::list. The end iterator must point "one past the last element" in the list. There exist implementations of std::list where what end points to is a dynamically allocated node. Even the default constructor allocates such a node so that when you call end(), there is something to point to.
In such an implementation, every constructor must allocate a node for end() to point to… even the move constructor. That allocation may fail, and throw an exception.
This same behavior can extend to any node-based container.
There are also implementations of these node-based containers that do a "short-string" optimization: They embed the end node within the container class itself, instead of dynamically allocating. Thus the default constructor (and move constructor) need not allocate anything.
The move assignment operator can throw for any container<X> if for the container's allocator propagate_on_container_move_assignment::value is false, and if the allocator in the lhs is not equal to the allocator in the rhs. In that case the move assignment operator is forbidden from transferring memory ownership from the rhs to the lhs. This can not happen if you are using std::allocator, as all instances of std::allocator are equal to one another.
Update
Here is a conforming and portable example of the case when propagate_on_container_move_assignment::value is false. It has been tested against the latest version of VS, gcc and clang.
#include <cassert>
#include <cstddef>
#include <iostream>
#include <vector>
template <class T>
class allocator
{
int id_;
public:
using value_type = T;
allocator(int id) noexcept : id_(id) {}
template <class U> allocator(allocator<U> const& u) noexcept : id_(u.id_) {}
value_type*
allocate(std::size_t n)
{
return static_cast<value_type*>(::operator new (n*sizeof(value_type)));
}
void
deallocate(value_type* p, std::size_t) noexcept
{
::operator delete(p);
}
template <class U, class V>
friend
bool
operator==(allocator<U> const& x, allocator<V> const& y) noexcept
{
return x.id_ == y.id_;
}
};
template <class T, class U>
bool
operator!=(allocator<T> const& x, allocator<U> const& y) noexcept
{
return !(x == y);
}
template <class T> using vector = std::vector<T, allocator<T>>;
struct A
{
static bool time_to_throw;
A() = default;
A(const A&) {if (time_to_throw) throw 1;}
A& operator=(const A&) {if (time_to_throw) throw 1; return *this;}
};
bool A::time_to_throw = false;
int
main()
{
vector<A> v1(5, A{}, allocator<A>{1});
vector<A> v2(allocator<A>{2});
v2 = std::move(v1);
try
{
A::time_to_throw = true;
v1 = std::move(v2);
assert(false);
}
catch (int i)
{
std::cout << i << '\n';
}
}
This program outputs:
1
which indicates that the vector<T, A> move assignment operator is copy/moving its elements when propagate_on_container_move_assignment::value is false and the two allocators in question do not compare equal. If any of those copies/moves throws, then the container move assignment throws.
Yes, throwing move constructors exist in the wild. Consider std::pair<T, U> where T is noexcept-movable, and U is only copyable (assume that copies can throw). Then you have a useful std::pair<T, U> move constructor which may throw.
There is a std::move_if_noexcept utility in the standard library if you need (useful to implement std::vector::resize with at least the basic exception guarantee).
See also Move constructors and the Strong Exception Guarantee
Move constructors on classes with const data members can also throw. Check here for more info.