I wanted to try to add a move assignment operator to my Mesh class below, knowing that a vector of Meshes appear as a field member in my Model class:
#include <vector>
struct Mesh {
std::vector<int> vertexes;
Mesh() {
}
Mesh& operator=(Mesh&& m) {
vertexes = std::move(m.vertexes);
return *this;
}
};
struct Model {
std::vector<Mesh> meshes;
Model() {
meshes.resize(10);
}
};
int main() {
Model model;
return 0;
}
This code doesn't work though. Below is the error I'm getting (clang was more explicit than gcc):
In file included from script.cpp:1:
In file included from /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.0/../../../../include/c++/12.2.0/vector:63:
/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.0/../../../../include/c++/12.2.0/bits/stl_uninitialized.h:90:7: error: static_assert failed due to requirement 'is_constructib
le<Mesh, Mesh &&>::value' "result type must be constructible from input type"
static_assert(is_constructible<_ValueType, _Tp>::value,
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.0/../../../../include/c++/12.2.0/bits/stl_uninitialized.h:182:4: note: in instantiation of function template specialization 's
td::__check_constructible<Mesh, Mesh &&>' requested here
= _GLIBCXX_USE_ASSIGN_FOR_INIT(_ValueType2, _From);
^
/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.0/../../../../include/c++/12.2.0/bits/stl_uninitialized.h:101:13: note: expanded from macro '_GLIBCXX_USE_ASSIGN_FOR_INIT'
&& std::__check_constructible<T, U>()
^
/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.0/../../../../include/c++/12.2.0/bits/stl_uninitialized.h:372:19: note: in instantiation of function template specialization '
std::uninitialized_copy<std::move_iterator<Mesh *>, Mesh *>' requested here
return std::uninitialized_copy(__first, __last, __result);
^
/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.0/../../../../include/c++/12.2.0/bits/stl_uninitialized.h:396:19: note: in instantiation of function template specialization '
std::__uninitialized_copy_a<std::move_iterator<Mesh *>, Mesh *, Mesh>' requested here
return std::__uninitialized_copy_a
^
/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.0/../../../../include/c++/12.2.0/bits/vector.tcc:674:14: note: in instantiation of function template specialization 'std::__un
initialized_move_if_noexcept_a<Mesh *, Mesh *, std::allocator<Mesh>>' requested here
std::__uninitialized_move_if_noexcept_a(
^
/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.0/../../../../include/c++/12.2.0/bits/stl_vector.h:1011:4: note: in instantiation of member function 'std::vector<Mesh>::_M_de
fault_append' requested here
_M_default_append(__new_size - size());
^
script.cpp:20:12: note: in instantiation of member function 'std::vector<Mesh>::resize' requested here
meshes.resize(10);
In file included from script.cpp:1:
In file included from /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.0/../../../../include/c++/12.2.0/vector:62:
/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.0/../../../../include/c++/12.2.0/bits/stl_construct.h:119:25: error: call to implicitly-deleted copy constructor of 'Mesh'
::new((void*)__p) _Tp(std::forward<_Args>(__args)...);
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.0/../../../../include/c++/12.2.0/bits/stl_uninitialized.h:120:11: note: in instantiation of function template specialization '
std::_Construct<Mesh, Mesh>' requested here
std::_Construct(std::__addressof(*__cur), *__first);
^
/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.0/../../../../include/c++/12.2.0/bits/stl_uninitialized.h:137:16: note: in instantiation of function template specialization '
std::__do_uninit_copy<std::move_iterator<Mesh *>, Mesh *>' requested here
{ return std::__do_uninit_copy(__first, __last, __result); }
^
/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.0/../../../../include/c++/12.2.0/bits/stl_uninitialized.h:185:2: note: in instantiation of function template specialization 's
td::__uninitialized_copy<false>::__uninit_copy<std::move_iterator<Mesh *>, Mesh *>' requested here
__uninit_copy(__first, __last, __result);
^
/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.0/../../../../include/c++/12.2.0/bits/stl_uninitialized.h:372:19: note: in instantiation of function template specialization '
std::uninitialized_copy<std::move_iterator<Mesh *>, Mesh *>' requested here
return std::uninitialized_copy(__first, __last, __result);
^
/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.0/../../../../include/c++/12.2.0/bits/stl_uninitialized.h:396:19: note: in instantiation of function template specialization '
std::__uninitialized_copy_a<std::move_iterator<Mesh *>, Mesh *, Mesh>' requested here
return std::__uninitialized_copy_a
^
/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.0/../../../../include/c++/12.2.0/bits/vector.tcc:674:14: note: in instantiation of function template specialization 'std::__un
initialized_move_if_noexcept_a<Mesh *, Mesh *, std::allocator<Mesh>>' requested here
std::__uninitialized_move_if_noexcept_a(
^
/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.0/../../../../include/c++/12.2.0/bits/stl_vector.h:1011:4: note: in instantiation of member function 'std::vector<Mesh>::_M_de
fault_append' requested here
_M_default_append(__new_size - size());
^
script.cpp:20:12: note: in instantiation of member function 'std::vector<Mesh>::resize' requested here
meshes.resize(10);
^
script.cpp:9:9: note: copy constructor is implicitly deleted because 'Mesh' has a user-declared move assignment operator
Mesh& operator=(Mesh&& m) {
^
I do realize that:
std::vector::resize() requires that the value_type is default constructible (hence the empty constructor Mesh::Mesh()).
The move assignment operator (like the move constructor) deletes the copy constructor and copy assignment operator.
My questions are:
Why is std::vector::resize() trying to call the deleted copy constructor to allocate space for new items, when an empty constructor is defined?
Is it failing because one of std::vector::resize()'s requirements isn't met?
If so how do I meet it without removing the move assignment? (if I do my code works, same with resize()).
std::vector::resize() requires T to be MoveInsertible and DefaultInsertable.
Per MoveInsertible:
If A is std::allocator<T>, then this will call placement-new, as by ::new((void*)p) T(rv) (until C++20) std::construct_at(p, rv) (since C++20). This effectively requires T to be move constructible.
If std::allocator<T> or a similar allocator is used, a class does not have to implement a move constructor to satisfy this type requirement: a copy constructor that takes a const T& argument can bind rvalue expressions. If a MoveInsertable class implements a move constructor, it may also implement move semantics to take advantage of the fact that the value of rv after construction is unspecified.
And per Move constructors:
Implicitly-declared move constructor
If no user-defined move constructors are provided for a class type (struct, class, or union), and all of the following is true:
there are no user-declared copy constructors;
there are no user-declared copy assignment operators;
there are no user-declared move assignment operators;
there is no user-declared destructor.
then the compiler will declare a move constructor as a non-explicit inline public member of its class with the signature T::T(T&&).
Your Mesh class does not have any user-declared copy/move constructors, but it does have a user-declared move assignment operator. As such, Mesh does not have a compiler-generated move constructor, so it is not move constructible, and thus does not satisfy the requirements of MoveInsertible, hence the compiler error.
To fix this without removing your move assignment operator, as you requested, you will need to add a user-declared copy constructor and/or move constructor to Mesh, eg:
struct Mesh {
std::vector<int> vertexes;
Mesh() {
}
Mesh(const Mesh& m) : vertexes(m.vertexes) {
}
// and/or:
Mesh(Mesh&& m) : vertexes(std::move(m.vertexes)) {
}
// in which case, you may as well consider adding this, too:
Mesh& operator=(const Mesh& m) {
if (this != &m) {
vertexes = m.vertexes;
}
return *this;
}
Mesh& operator=(Mesh&& m) {
vertexes = std::move(m.vertexes);
return *this;
}
};
However, std::vector is fully copyable and movable by itself, so if you can forget your unnecessary requirement and get rid of the move assignment operator altogether, then all of the compiler-generated constructors and assignment operators will suffice for this example:
struct Mesh {
std::vector<int> vertexes;
};
Your class Mesh declares an explicit default constructor, which by default, deletes the move constructor.
Either do not declare a constructor, or declare all these 5 functions:
default constructor
copy constructor
move constructor
copy operator
move operator
In most cases, but not all, defaults should work fine.
Example:
#include <vector>
// Quick explanation of the rules of 0, 3 and 5
struct Mesh_rule_of_0 {
std::vector<int> vertices;
// rule of zero. No explicit constructors, nor operator=() defined.
// The compiler will generate implicit defaults for you.
};
struct Model_0 {
std::vector<Mesh_rule_of_0> meshes;
Model_0() { meshes.resize(10); }
};
struct Mesh_rule_of_3 {
std::vector<int> vertices;
// if one of these 3 is defined, then all three must be defined
// The compiler will _try_ to generate implicit move operations for you.
Mesh_rule_of_3() = default;
Mesh_rule_of_3(const Mesh_rule_of_3&) = default;
Mesh_rule_of_3& operator=(const Mesh_rule_of_3&) = default;
};
struct Model_3 {
std::vector<Mesh_rule_of_3> meshes;
Model_3() { meshes.resize(10); }
};
struct Mesh_rule_of_5 {
std::vector<int> vertices;
// rule of 5, if a move operator is defined, all 5 must be
// defined.
Mesh_rule_of_5() = default;
Mesh_rule_of_5(const Mesh_rule_of_5&) = default;
Mesh_rule_of_5(Mesh_rule_of_5&&) = default;
Mesh_rule_of_5& operator=(const Mesh_rule_of_5&) = default;
Mesh_rule_of_5& operator=(Mesh_rule_of_5&&) = default;
};
struct Model_5 {
std::vector<Mesh_rule_of_5> meshes;
Model_5() { meshes.resize(10); }
};
struct Mesh_fail {
std::vector<int> vertices;
// this will fail, because it doesn't satisfy the requirements
// of the rule of 0, nor of the rule of 3 nor of
// the rule of 5
Mesh_fail() {}
Mesh_fail& operator=(Mesh_fail&&) = default;
};
// uncomment for failure
// struct Model_fail {
// std::vector<Mesh_fail> meshes;
// Model_fail() { meshes.resize(10); }
// };
int main() {
Model_0 m0;
Model_3 m3;
Model_5 m5;
// uncomment for failure
// Model_fail mf;
return 0;
}
I apologize for the wordy title, but I'm having trouble concisely expressing this question.
I have a class with a deleted copy constructor and copy assignment operator. When I attempt to initialize an array with instances of the class I get a compiler error unless I provide a move constructor. However, the provided move constructor is not actually invoked.
Here's a MWE illustrating the issue.
#include <cstdio>
#include <string>
class A
{
public:
explicit A(std::string s) : s_(s) { puts("constructor"); }
A() = delete;
~A() = default;
A(const A &other) = delete;
A &operator=(const A &other) = delete;
// A(A &&other) : s_(std::move(other.s_)) { puts("move constructor"); }
void print_s() { printf("%s\n", s_.c_str()); }
private:
std::string s_;
};
int main()
{
A arr[]{A{"some"}, A{"string"}};
for (auto &a : arr) {
a.print_s();
}
}
With the move constructor commented out as shown, I get the error:
> g++ file.cc -g -std=c++11 -o file && ./file
file.cc: In function ‘int main()’:
file.cc:22:32: error: use of deleted function ‘A::A(const A&)’
22 | A arr[]{A{"some"}, A{"string"}};
| ^
file.cc:10:2: note: declared here
10 | A(const A &other) = delete;
| ^
file.cc:22:32: error: use of deleted function ‘A::A(const A&)’
22 | A arr[]{A{"some"}, A{"string"}};
| ^
file.cc:10:2: note: declared here
10 | A(const A &other) = delete;
| ^
If I uncomment the move constructor I get the output
constructor
constructor
some
string
So, the move constructor does not actually appear to be invoked.
Why is the move constructor needed? Have I made a mistake somewhere (e.g., a poorly implemented move constructor)?
With
A arr[]{A{"some"}, A{"string"}};
You are creating two temporary A objects and then copying/moving them into the array. Now, the compiler can optimize this copy/move away, but that class still needs to be copyable/moveable in order for this to happen. When you comment out your move constructor, the class is no longer copyable or moveble, so you get a compiler error.
It should be noted that this changed in C++17 with its guaranteed copy elision. The code will compile in C++17 and beyond for types that can't be copied or moved.
If you change
explicit A(std::string s) : s_(s) { puts("constructor"); }
to
A(std::string s) : s_(s) { puts("constructor"); }
then your main function could become
int main()
{
A arr[]{{"some"}, {"string"}};
for (auto &a : arr) {
a.print_s();
}
}
which will compile even if you can't copy or move an A.
Consider the following code:
#include <iostream>
#include <thread>
int main() {
std::thread t;
const auto l = [x = std::move(t)]{};
decltype(l) m = std::move(l);
}
This code doesn't compile with the following messages:
prog.cc: In function 'int main()':
prog.cc:7:32: error: use of deleted function 'main()::<lambda()>::<lambda>(const main()::<lambda()>&)'
7 | decltype(l) m = std::move(l);
| ^
prog.cc:6:37: note: 'main()::<lambda()>::<lambda>(const main()::<lambda()>&)' is implicitly deleted because the default definition would be ill-formed:
6 | const auto l = [x = std::move(t)]{};
| ^
prog.cc:6:37: error: use of deleted function 'std::thread::thread(const std::thread&)'
In file included from prog.cc:2:
/opt/wandbox/gcc-head/include/c++/10.0.1/thread:154:5: note: declared here
154 | thread(const thread&) = delete;
| ^~~~~~
Is there a way to make lambda non-copyable or non-movable without explicit capturing an any non-copyable variable (i.e. leaving [] empty)?
You can write a simple enveloping aggregate that will prevent the move and copy.
struct NoCopyMove {
NoCopyMove(NoCopyMove const&) = delete;
NoCopyMove(NoCopyMove&&) = delete;
void operator=(NoCopyMove const&) = delete;
void operator=(NoCopyMove&&) = delete;
};
template<class Functor>
struct Fixed : Functor, NoCopyMove {
using Functor::operator();
};
template<typename F>
Fixed (F&&) -> Fixed<std::decay_t<F>>;
To be used like this
const auto l = Fixed{[]{}};
NoCopyMove is a simple mixin that disables copying and moving. Writing it this way allows us to keep the specializations of Fixed as simple aggregates.
All Fixed does is inherit/initialize as base (with guaranteed copy elision when possible) the functor it's being given as an argument. And then expose its operator().
Since there are no members involved (other then maybe in Functor), and since a lambda cannot possibly inherit from our custom NoCopyMove class, the empty base optimization kicks in for stateless lambdas. So objects are not expected to be any larger than the lambda you initialize them with.
I failed to understand the steps of compilation of this code.
First, where is what is purpose of the default constructor, and why so many objects of type MyClass?
link to sololearn where I saved the code
#include <iostream>
using namespace std;
class MyClass {
public:
int var;
MyClass() { }
MyClass(int a)
: var(a) { }
MyClass operator+(MyClass &obj) {
MyClass res;
res.var= this->var+obj.var;
//'this' is refering to active (obj1)
return res;
}
};
int main() {
MyClass obj1(12), obj2(55);
MyClass res = obj1+obj2;
cout << res.var;
}
//I've not understood, its from a lesson
If you suspect you don't need a piece of code, try compiling without it and you'll know pretty quickly whether your suspicion was correct or not.
When you remove the default constructor in the class you provided here's what happens:
..\Playground\: In member function 'MyClass MyClass::operator+(MyClass&)':
..\Playground\:11:21: error: no matching function for call to 'MyClass::MyClass()'
MyClass res;
^~~
..\Playground\:7:9: note: candidate: MyClass::MyClass(int)
MyClass(int a)
^~~~~~~
..\Playground\:7:9: note: candidate expects 1 argument, 0 provided
..\Playground\:4:7: note: candidate: constexpr MyClass::MyClass(const MyClass&)
class MyClass {
Which is to say the problem is in the +operator implementation at the line MyClass res; . This line creates an instance of the object MyClassusing the default constructor, so there's no way the code snippet your provided works without a default constructor.
The constructor without parameters is for the object that doesn't pass any value, the one that stores the sum of the two operand objects. ---simple---
I have a assignment operator.
AP<T>& operator=(AP<T>& o) {Super::operator=(o); return *this; }
When I expect the g++ compiler finds this operator for this code.
AP<System> to = System::Create();
I got compilation error.
no matching function for call to ‘H::AP<H::System>::AP(H::AP<H::System>)’
ap.cpp:13: note: candidates are: H::AP<T>::AP(H::AP<U>&) [with U = H::System, T = H::System]
ap.cpp:12: note: H::AP<T>::AP(H::AP<T>&) [with T = H::System]
ap.cpp:11: note: H::AP<T>::AP(T*) [with T = H::System]
Why is this? MSVC compiles this code without problem.
The source is as follows.
#include <memory>
namespace H {
template<typename T>
class AP : public std::auto_ptr<T>
{
typedef std::auto_ptr<T> Super;
public:
template<typename U> AP<T>& operator=(AP<U>& o) { Super::operator=(o.release()); return *this; }
AP<T>& operator=(AP<T>& o) { Super::operator=(o); return *this; }
};
class System {
public:
static AP<System> Create();
};
AP<System> System::Create()
{
AP<System> a(new System());
return a;
}
int main()
{
AP<System> to = System::Create();
}
};
ADDED
With AP(const AP<T>& o) : Super(o) { }, I got those errors.
ap.cpp: In copy constructor ‘H::AP<T>::AP(const H::AP<T>&) [with T = H::System]’:
ap.cpp:33: instantiated from here
ap.cpp:12: error: passing ‘const H::AP<H::System>’ as ‘this’ argument of ‘std::auto_ptr<_Tp>::operator std::auto_ptr_ref<_Tp1>() [with _Tp1 = H::System, _Tp = H::System]’ discards qualifiers
ADDED2
I don't know it's the best solution, but this code seems to work.
int main()
{
H::AP<H::System> tox(H::System::Create().release());
return 0;
}
AP<System> to = System::Create(); looks for a copy constructor as Adam said.
Using const will solve your problem. Apart from that temporaries cannot be bound to non-const references.
For example:
AP<System> &ref = AP<System>();
won't compile on gcc if the parameter of your copy c-tor is not a reference to const. However on MSVC++ the above code would compile because MSVC++(2008 or before) allows temporaries to get bound to non-const reference (evil extension).
However if you try to replicate auto_ptr's copy c-tor you need to implement something similar to auto_ptr_ref (in order to allow copying temporary auto_ptr objects)
It's looking for a copy constructor, not an operator=: read the error message more carefully. Your copy constructor is not defined correctly: you need to defined it as a const reference:
AP(const AP<T>& o) : Super(o) { }
// ^^^^^