std::swap for non copiable but movable struct - c++

According to C++ reference std::swap is equivalent to
T c(std::move(a)); a=std::move(b); b=std::move(c);
This should allow to swap two non copiable but movable object. Therefore I don't understand why
#include<utility>
struct Foo {
Foo() = delete;
Foo(int) {};
Foo(Foo &) = delete;
Foo(Foo &&) {};
~Foo() {};
};
int main() {
Foo a(1),b(2);
std::swap(a,b);
}
is refused by the compiler with
In file included from /usr/include/c++/4.8/bits/stl_pair.h:59:0,
from /usr/include/c++/4.8/utility:70,
from swap.cpp:1:
/usr/include/c++/4.8/bits/move.h: In instantiation of ‘void std::swap(_Tp&, _Tp&) [with _Tp = Foo]’:
swap.cpp:13:16: required from here
/usr/include/c++/4.8/bits/move.h:176:11: error: use of deleted function ‘Foo& Foo::operator=(const Foo&)’
__a = _GLIBCXX_MOVE(__b);
^
swap.cpp:3:8: note: ‘Foo& Foo::operator=(const Foo&)’ is implicitly declared as deleted because ‘Foo’ declares a move constructor or move assignment operator
struct Foo {
^
In file included from /usr/include/c++/4.8/bits/stl_pair.h:59:0,
from /usr/include/c++/4.8/utility:70,
from swap.cpp:1:
/usr/include/c++/4.8/bits/move.h:177:11: error: use of deleted function ‘Foo& Foo::operator=(const Foo&)’
__b = _GLIBCXX_MOVE(__tmp);
note: this is with GCC 4.8 and 4.9 but clang complain as well.

You declared a move constructor. However, you need a move assignment operator for std::swap. You should add the following two operators:
auto operator=(const Foo& rhs) & -> Foo& = delete;
auto operator=(Foo&& rhs) & noexcept -> Foo&
{
// ...
return *this;
}

Related

User defined operator conversion resolution order

Is it possible to configure the resolution order of user defined operators? Consider the following code:
#include <memory>
#include <utility>
using P = std::unique_ptr<int>;
struct S{
P p;
operator P() && { return std::move(p); }
operator const P&() const { return p; }
};
S s{std::make_unique<int>()};
P p;
p = std::move(s);
This fails to compile:
In file included from /opt/compiler-explorer/gcc-12.1.0/include/c++/12.1.0/memory:76,
from <source>:1:
/opt/compiler-explorer/gcc-12.1.0/include/c++/12.1.0/bits/unique_ptr.h:406:19: note: candidate: 'std::unique_ptr<_Tp, _Dp>& std::unique_ptr<_Tp, _Dp>::operator=(std::unique_ptr<_Tp, _Dp>&&) [with _Tp = int; _Dp = std::default_delete<int>]'
406 | unique_ptr& operator=(unique_ptr&&) = default;
| ^~~~~~~~
/opt/compiler-explorer/gcc-12.1.0/include/c++/12.1.0/bits/unique_ptr.h:515:19: note: candidate: 'std::unique_ptr<_Tp, _Dp>& std::unique_ptr<_Tp, _Dp>::operator=(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = int; _Dp = std::default_delete<int>]' (deleted)
515 | unique_ptr& operator=(const unique_ptr&) = delete;
It seems compilation is failing because of an ambiguity between the deleted copy assignment and defaulted move assignment operators.
Why is the compiler unable to resolve the operation here? Is there a way to define the operators to make this work?
If these operators are supposed to be conversion operators, you can define them like this:
operator P() && { return std::move(p); }
operator const P&() & { return p; }
Demo
The second operator needs the lvalue ref-qualifier (the & at the end) because without it, the implicit object parameter can also be an rvalue. That case is also covered with the rvalue ref-qualified version, so the call becomes ambiguous. Same thing if you add the const qualifier, because an rvalue can bind to a const lvalue reference.
You didn't define conversion/cast operators, you defined call operators. As written, you'd need to do:
p = std::move(s)();
to call s.
If you want a user-defined conversion, you'd want to change:
P operator() && { return std::move(p); }
const P& operator() const { return p; }
to something like:
operator P() && { return std::move(p); }
operator P() const { return p; }
though I'm not sure whether that paired overload of a conversion operator is legal.

Inaccessible operator == breaks child class with spaceship operator

I bumped in GCC error, which can be demonstrated with the following simplified program. Base struct A has an operator == with some argument type other than A. Child struct B defines default spaceship operator <=>, which shall implicitly define operator == as well. But since no appropriate equality operator is found in the base class, B::operator == must be implicitly deleted. Instead, the resulting struct B appears broken in a way that no object of it can be created:
#include <compare>
struct A {
std::strong_ordering operator <=>(const A &) const = default;
bool operator==(int) const;
};
struct B : A {
virtual std::strong_ordering operator <=>(const B &) const noexcept = default;
};
B b; //GCC error here
Demo: https://gcc.godbolt.org/z/584nhfxxs
Error message:
error: use of deleted function 'virtual constexpr bool B::operator==(const B&) const'
9 | virtual std::strong_ordering operator <=>(const B &) const noexcept = default;
| ^~~~~~~~
<source>:9:34: note: 'virtual constexpr bool B::operator==(const B&) const' is implicitly deleted because the default definition would be ill-formed:
<source>:8:8: error: no match for 'operator==' (operand types are 'A' and 'A')
8 | struct B : A {
| ^
<source>:5:10: note: candidate: 'bool A::operator==(int) const' (reversed)
5 | bool operator==(int) const;
| ^~~~~~~~
<source>:5:21: note: no known conversion for argument 1 from 'A' to 'int'
5 | bool operator==(int) const;
Is it simply a bug in GCC?

User-defined conversion operator doesn't work on references

I have a simple primitive type wrapper:
template <typename T>
class Scalar {
public:
explicit Scalar(T value) : value{value} {}
Scalar(Scalar&& other) = default;
Scalar& operator=(Scalar&& other) = default;
Scalar(const Scalar& other) = default;
Scalar& operator=(const Scalar& other) = default;
template <typename U>
explicit operator Scalar<U>() {
return Scalar<U>{static_cast<U>(this->value)};
}
inline T getValue() const noexcept { return this->value; }
private:
T value;
};
Casting Scalar values works well, but somehow it fails for references, e.g.
auto a = Scalar<double>{2.54};
Scalar<int> b = static_cast<Scalar<int>>(a); // works
const auto& c = a;
Scalar<int> d = static_cast<Scalar<int>>(c); // fails
Here's the compilation error (can be checked here http://rextester.com/GOPYU13091), any ideas what might be the issue here?
source_file.cpp: In function ‘int main()’:
source_file.cpp:31:47: error: no matching function for call to ‘Scalar(const Scalar<double>&)’
Scalar<int> d = static_cast<Scalar<int>>(c);
^
source_file.cpp:12:3: note: candidate: Scalar<T>::Scalar(const Scalar<T>&) [with T = int] <near match>
Scalar(const Scalar& other) = default;
^
source_file.cpp:12:3: note: conversion of argument 1 would be ill-formed:
source_file.cpp:31:47: error: could not convert ‘c’ from ‘const Scalar<double>’ to ‘const Scalar<int>&’
Scalar<int> d = static_cast<Scalar<int>>(c);
^
source_file.cpp:10:3: note: candidate: Scalar<T>::Scalar(Scalar<T>&&) [with T = int] <near match>
Scalar(Scalar&& other) = default;
^
source_file.cpp:10:3: note: conversion of argument 1 would be ill-formed:
source_file.cpp:31:47: error: could not convert ‘c’ from ‘const Scalar<double>’ to ‘Scalar<int>&&’
Scalar<int> d = static_cast<Scalar<int>>(c);
^
This is a const vs non-const issue, not a reference vs object issue.
Using
auto& c = a;
Scalar<int> d = static_cast<Scalar<int>>(c);
works for me.
By changing the user defined conversion operator to const member function
template <typename U>
explicit operator Scalar<U>() const {
return Scalar<U>{static_cast<U>(this->value)};
}
also makes sure that the following works.
const auto& c = a;
Scalar<int> d = static_cast<Scalar<int>>(c);

Clash when using trivially copy/move-assignable union in constexpr

Considering the following code
struct S
{
constexpr S() = default;
constexpr S(S const &) = default;
constexpr S(S &) = default;
constexpr S(S &&) = default;
#if 1
S & operator = (S const &) = default;
S & operator = (S &) = default;
S & operator = (S &&) = default;
#else
constexpr S & operator = (S const &) = default;
constexpr S & operator = (S &) = default;
constexpr S & operator = (S &&) = default;
#endif
~S() = default;
};
struct U
{
union
{
S s;
};
constexpr U() : s{} { ; }
};
inline constexpr bool test()
{
U v;
U w;
v = w;
return true;
}
static_assert(test());
I found out that there is contradiction.
I want to use union (or euqally union-like class) in constexpr.
Version #if 1 gives an error (error: constexpr function never produces a constant expression):
main.cpp:31:23: error: constexpr function never produces a constant expression [-Winvalid-constexpr]
inline constexpr bool test()
^
main.cpp:35:7: note: non-constexpr function 'operator=' cannot be used in a constant expression
v = w;
^
main.cpp:19:8: note: declared here
struct U
^
main.cpp:39:15: error: static_assert expression is not an integral constant expression
static_assert(test());
^~~~~~
main.cpp:35:7: note: non-constexpr function 'operator=' cannot be used in a constant expression
v = w;
^
main.cpp:39:15: note: in call to 'test()'
static_assert(test());
^
main.cpp:19:8: note: declared here
struct U
^
2 errors generated.
But version #if 0 also give an error (error: defaulted definition of copy assignment operator is not constexpr):
main.cpp:12:5: error: defaulted definition of copy assignment operator is not constexpr
constexpr S & operator = (S const &) = default;
^
main.cpp:13:5: error: defaulted definition of copy assignment operator is not constexpr
constexpr S & operator = (S &) = default;
^
main.cpp:14:5: error: defaulted definition of move assignment operator is not constexpr
constexpr S & operator = (S &&) = default;
^
main.cpp:35:7: error: object of type 'U' cannot be assigned because its copy assignment operator is implicitly deleted
v = w;
^
main.cpp:24:11: note: copy assignment operator of 'U' is implicitly deleted because field 's' has no copy assignment operator
S s;
^
4 errors generated.
LIVE EXAMPLE
All above is for version of clang 3.7.0, but newer version clang 3.8.0 (trunk 253951) nothing says about constexpr S & operator = (S const &) = default; (it allows to compile such a code), but consequences are the same.
For trivial copy/move-constructors, but user-provided assignment operators analogous code (construction) compiles fine.
I think the problem (bug) is in user-declared special functions, but it should be legal at all.
Also please look at the last and last but one clauses of this code. Quite well-designed (at my mind) variant becomes useless in constexpr, due to difference in interpretation by compiler of next definitions:
struct S {};
and:
struct S
{
constexpr S() = default;
constexpr S(S const &) = default;
constexpr S(S &) = default;
constexpr S(S &&) = default;
S & operator = (S const &) = default;
S & operator = (S &) = default;
S & operator = (S &&) = default;
~S() = default;
};
But both is equivalent at my mind (the second recognized by compiler as not POD, but who cares?).
Why defaulted assignment operator of empty struct is not constexpr?
UPDATE:
Removing the declaration of destructor makes problem to disappear. So even user-declared (not user-provided) destructor make implicitly defined defaulted assignment operators to be not marked as constexpr and to be non-trivial. It definitely bug because no one source telling about this.

Equality operator (==) unsupported on map iterators for non-copyable maps

When I have a map of non-copyable objects, why can't I compare the iterators using ==? I would imagine it does not need to copy (or move) the actual objects when doing equality testing on the iterators.
#include <iostream>
#include <utility>
#include <map>
using namespace std;
class A {
private:
int i;
public:
A(int i) : i(i) {}
A(A&) = delete;
A(A&& a) : i(a.i) {}
~A() {}
A& operator=(A&) = delete;
bool operator==(const A& a) const { return i == a.i; }
};
int main() {
map<int, A> myMap;
map<int, A>::iterator it = myMap.find(1);
cout << (it == myMap.end()) << endl;
}
This example fails to compile, giving an error on the line with the cout.
g++ gives this error:
/usr/include/c++/4.8.2/bits/stl_pair.h: In instantiation of ‘struct std::pair<const int, A>’:
test2.cpp:24:27: required from here
/usr/include/c++/4.8.2/bits/stl_pair.h:127:17: error: ‘constexpr std::pair<_T1, _T2>::pair(const std::pair<_T1, _T2>&) [with _T1 = const int; _T2 = A]’ declared to take const reference, but implicit declaration would take non-const
constexpr pair(const pair&) = default;
clang++ gives this error:
/usr/bin/../lib64/gcc/x86_64-unknown-linux-gnu/4.8.2/../../../../include/c++/4.8.2/bits/stl_pair.h:127:17: error: the parameter for this explicitly-defaulted copy constructor is const, but a member or base requires it to be non-const
constexpr pair(const pair&) = default;
^
test2.cpp:24:14: note: in instantiation of template class 'std::pair<const int, A>' requested here
cout << (it == myMap.end()) << endl;
However, it does work if a map<int, int> is used instead of a map<int, A>. Using a const map<int, A> with map<int, A>::const_iterator does not help.
I tried looking up the exact signature of map::iterator::operator== on cppreference, (map::iterator is a BidirectionalIterator, which is a ForwardIterator, which is an InputIterator) but the website is vague about the exact type signatures in the concepts.
The problem is with your deleted methods to make A noncopyable ...
#include <iostream>
#include <utility>
#include <map>
using namespace std;
class A {
private:
int i;
public:
A(int i) : i(i) {}
// A(A&) = delete; should be ...
A(const A&) = delete;
A(A&& a) : i(a.i) {}
~A() {}
// A& operator=(A&) = delete; should be ...
A& operator=(const A&) = delete;
bool operator==(const A& a) const { return i == a.i; }
};
int main() {
map<int, A> myMap;
map<int, A>::iterator it = myMap.find(1);
cout << (it == myMap.end()) << endl;
}
Verified this with gcc/g++ 4.6.3
Removing the "const" from the copy constructor and the assignment operator caused my compiler to give the same error which you received.
I would have to look up the details/references as to why those are declared that way, but the copy constructor and the assignment operator always take a const reference (make sense as the value to which your instance is being assigned should not be modified by the method).