I'm working on making some changes to a piece of code, and have a doubt in understanding the behavior of std::move in below case:
struct Timer {
Timer (boost::asio::io_service& ios) : timer_{ios} {}
boost::asio::steady_timer timer_;
};
struct TimerContext {
void *ctxt_;
};
class A {
std::function<void(Timer *, const TimerContext&)> callback_;
boost::asio::io_service ios_;
};
A's constructor:
A::A (std::function<void(Timer *, const TimerContext&)> cb) : callback_{std::move(cb)}
{
}
The callback function definition:
void
customCallback (Timer *timer, const TimerContext& ctxt) {
...
}
In main(),
for (int i = 0; i < 5; ++i) {
A *a = new A{customCallback};
}
My doubt is for this line:
A::A (std::function<void(Timer *, const TimerContext&)> cb) : callback_{std::move(cb)}
Class A is getting instantiated in a loop, and the same customCallback function is getting moved to the custom constructor for each new object.
Will the first std::move not make callback function unusable for next call? As I understood, if you use std::move(t), then t cannot be used again in that scope.
I'm confused what is happening internally here for all 5 calls to std::move(cb) on the same function in new A. Is this the right way to do how it is implemented?
Look carefully at A's constructor:
A::A (std::function<void(Timer *, const TimerContext&)> cb)
The function, cb is being passed by value. That means a copy of the function has already occurred from when it was invoked via new:
A *a = new A{customCallback};
The std::move in the constructor initializer list exists to avoid a redundant copy into the member variable. The original function defined by the caller who invoked new A remains unmoved. This is preferred because copying a std::function variable can be expensive. (sizeof(cb) - might be way bigger than you expected).
An alternative implementation: The function could have been passed as a const reference and allow the copy to occur in the constructor itself:
A::A (const std::function<void(Timer *, const TimerContext&)>& cb) : callback_{cb}
Your constructor A::A (std::function<void(Timer *, const TimerContext&)> cb) takes its parameter by value, i.e. customCallback is first copied into cb, then moved from that copy into callback_.
So every time the constructor is called, the copy is moved from, not the original customCallback object.
Also, you should avoid using raw calls to new and prefer smart pointers instead to avoid memory leaks, but that was not the question at hand here :-)
Related
I am trying to come up with the most efficient (i.e. minimal amount of copies) implementation of the following. I have a queue which is used to execute std::function<void()> objects at some later point in time. The queue can only take trivial data types and thus I need to pass it a pointer to a std::function rather than the std::function itself (i.e. the queue holds std::function<void()>* types).
Ideally, if a piece of data is captured (by value) in a lambda, only a SINGLE copy of this data should be made throughout the process of creating an std::function and adding it to the queue.
Here is an example I have been playing with:
std::function<void()>* invoke(const std::function<void()>& fn) {
printf("invoke()\r\n");
return new std::function<void()>(fn);
}
which I would use like so
class Base {
public:
virtual void sayHello() const = 0;
};
class A : public Base {
public:
A() {
printf("A::A()\r\n");
}
A(const A& a) {
printf("A copy constructor\r\n");
}
A(const A&& a) {
printf("A const move constructor\r\n");
}
A(A&& a) {
printf("A move constructor\r\n");
}
A& operator=(A&& other)
{
printf("A move assignment operator\r\n");
return other;
}
void sayHello() const override { printf("A says hello\r\n"); }
};
int main() {
A myA;
printf("invoking lambda which calls myA.sayHello()\r\n");
std::function<void()> *fn = invoke([myA](){
myA.sayHello();
});
return 0;
}
Since the lambda is capturing the object (myA) by value, a copy is made when the lambda is originally created. Now, since the lambda you see in main() is "temporary" (only used in the call to invoke) there should really only ever by a single copy of the lambda hanging around and thus only a single (additional) copy of myA.
However, here is an example of my output:
invoking lambda which calls myA.sayHello()
A copy constructor
A move constructor
invoke()
A copy constructor
It appears that, when I create a new std::function on the heap, it is COPYING the lambda (and thus myA) instead of just moving it, which is what my desired behavior is since the original lambda and std::function (the one created automatically when passing the lambda to the invoke function) are only temporary anyways.
I feel like I am very close, but am misunderstanding something here. Could anyone help?
Thanks!
EDIT
Based upon all the discussion here, I have made a few modifications to my code. Now I am stepping this up to a more complicated example as follows:
//Same A and Base classes as above
inline static void invoke(std::function<void()> fn) {
printf("invoke(std::function<void()> fn)\r\n");
std::function<void()>* newF = new std::function<void()>(std::move(fn));
(*newF)();
delete newF;
printf("return\r\n");
}
int main() {
printf("Starting Tests...\r\n");
A myA;
invoke([myA](){
myA.sayHello();
});
return 0;
}
Which has the following, unexpected output:
Starting Tests...
A::A()
A copy constructor
A move constructor
invoke(std::function<void()> fn)
A says hello
A::~A()
return
A::~A()
A::~A()
I can't figure out why there is a 3rd call to the destructor since only 2 copies of the A object should be in existence (the original copy in main() and the copy made by the copy constructor in the lambda).
Your example does not work because you are trying to std::move from a const reference. This will result in a type std::function<...> const&&, but the move constructor of std::function only accepts std::function<...>&& (without the const). This is not unusual in C++, as const rvalue references are a weird corner case of the language that don't make much sense conceptually. In particular, you cannot reasonably move from a const rvalue reference, because you won't be allowed to make any changes to the source.
If the intention is to guarantee that no copies happen, you should accept the argument via rvalue reference:
std::function<void()>* invoke(std::function<void()>&& fn) {
printf("invoke()\r\n");
return new std::function<void()>(std::move(fn));
}
Note that this restricts the function to only work with rvalue arguments. If you prefer a more flexible design, consider max66's answer.
Maybe... using perfect forwarding you can transform a copy in a move.
template <typename F>
auto invoke (F && fn) {
printf("invoke()\r\n");
return new std::function<void()>(std::forward<F>(fn));
}
You get
A::A()
invoking lambda which calls myA.sayHello()
A copy constructor
invoke()
A move constructor
A move constructor
Unrequested Suggestion: avoid like the plague direct memory allocation management; use smart pointer instead
template <typename F>
auto invoke (F && fn) {
printf("invoke()\r\n");
return std::unique_ptr<std::function<void()>>
{new std::function<void()>(std::forward<F>(fn))};
}
I have the following code which seems to work always (msvc, gcc and clang).
But I'm not sure if it is really legal. In my framework my classes may have "two constructors" - one normal C++ constructor which does simple member initialization and an additional member function "Ctor" which executes additional initialization code. It is used to allow for example calls to virtual functions. These calls are handled by a generic allocation/construction function - something like "make_shared".
The code:
#include <iostream>
class Foo
{
public:
constexpr Foo() : someConstField(){}
public:
inline void Ctor(int i)
{
//use Ctor as real constructor to allow for example calls to virtual functions
const_cast<int&>(this->someConstField) = i;
}
public:
const int someConstField;
};
int main()
{
//done by a generic allocation function
Foo f;
f.Ctor(12); //after this call someConstField is really const!
//
std::cout << f.someConstField;
}
Modifying const memory is undefined behaviour. Here that int has already been allocated in const memory by the default constructor.
Honestly I am not sure why you want to do this in the first place. If you want to be able to initalise Foo with an int just create an overloaded constructor:
...
constexpr Foo(int i) : someConstField{i} {}
This is completely legal, you are initalising the const memory when it is created and all is good.
If for some reason you want to have your object initalised in two stages (which without a factory function is not a good idea) then you cannot, and should not, use a const member variable. After all, if it could change after the object was created then it would no longer be const.
As a general rule of thumb you shouldn't have const member variables since it causes lots of problems with, for example, moving an object.
When I say "const memory" here, what I mean is const qualified memory by the rules of the language. So while the memory itself may or may not be writable at the machine level, it really doesn't matter since the compiler will do whatever it likes (generally it just ignores any writes to that memory but this is UB so it could do literally anything).
No.
It is undefined behaviour to modify a const value. The const_cast itself is fine, it's the modification that's the problem.
According to 7.1.6.1 in C++17 standard
Except that any class member declared mutable (7.1.1) can be modified, any attempt to modify a const
object during its lifetime (3.8) results in undefined behavior.
And there is an example (similar to yours, except not for class member):
const int* ciq = new const int (3); // initialized as required
int* iq = const_cast<int*>(ciq); // cast required
*iq = 4; // undefined: modifies a const object
If your allocation function allocates raw memory, you can use placement new to construct an object at that memory location. With this you must remember to call the destructor of the object before freeing the allocation.
Small example using malloc:
class Foo
{
public:
constexpr Foo(int i) : someConstField(i){}
public:
const int someConstField;
};
int main()
{
void *raw_memory = std::malloc(sizeof(Foo));
Foo *foo = new (raw_memory) Foo{3}; // foo->someConstField == 3
// ...
foo->~Foo();
std::free(foo);
}
I suggest, that you use the constructor to avoid the const cast. You commented, that after your call of Ctor the value of someConstField will remain const. Just set it in the constructor and you will have no problems and your code becomes more readable.
#include <iostream>
class Foo
{
public:
constexpr Foo(int i) : someConstField(Ctor(i)){}
int Ctor(); // to be defined in the implementation
const int someConstField;
};
int main()
{
Foo f(12);
std::cout << f.someConstField;
}
Lets consider the following piece of code:
template<typename T>
void f(std::unique_ptr<T>&& uptr) { /*...*/ }
In another function:
void g()
{
std::unique_ptr<ANY_TYPE> u_ptr = std::make_unique<ANY_TYPE>();
f(std::move(u_ptr));
X: u_ptr->do_sth(); // it works, I don't understand why. Details below.
}
I don't understand why u_ptr in line X is still alive.
After all I forced him to be moved (std::move).
---EDIT---
Ok, so now:
The code is still working:
class T{
public:
T(){}
void show(){
std::cout << "HEJ!\n";
}
};
void f(std::unique_ptr<T> ref){
ref->show();
}
int main()
{
std::unique_ptr<T> my;
my->show();
f(std::move(my));
my->show(); // How is it possible. Now, f takes unique_ptr by value
return 0;
}
You didn't show us that code to function f, but presumably it didn't move the pointer, even though it had permission to.
You passed the unique_ptr by reference. If function invocation actually moved it, then the function couldn't use it because it would be gone before the function had a chance to.
If you want function invocation to actually move the pointer, you need to pass the pointer by value, not be reference. That value would be a unique_ptr for it to be moved into. In that case, you should declare the function as taking a std::unique_ptr<T> instead of a std::unique_ptr<T>&&. Then you can actually invoke the move constructor when you call the function.
Update: With your latest change, the unique_ptr would no longer reference any valid object due to the move construction. You just never check that it does. Invoking a non-virtual method that doesn't access any member variables can work just the same whether the object is valid or destroyed because it doesn't need anything from the object. You also never made the unique_ptr actually point to anything.
Instead, make the unique_ptr point to something. After it's moved, try calling a virtual function or accessing a member whose value is changed by the destructor. Like this:
#include <iostream>
#include <memory>
class T{
public:
T() : valid (true) {}
~T() { valid = false; }
bool valid;
void show(){
std::cout << "HEJ! " << valid << std::endl;
}
};
void f(std::unique_ptr<T> ref){
ref->show();
}
int main()
{
std::unique_ptr<T> my (new T); // Make it point to a new object
my->show();
f(std::move(my));
my->show(); // Try to access
return 0;
}
in the line f(std::unique_ptr<T>&& uptr) uptr is not an object - it's a reference. a reference which capable to catch temporeries and mutate them.
it's like asking why doesn't the object get cloned in the next example
void func(std::string& str);
std::string str_ = "yyy";
func(str_);
str_ is passed by "regular" reference and won't get copied - this is what pass by reference means.
std::move only cast l-value to r-value-reference, which uptr in f(std::unique_ptr<T>&& uptr) can reference, it's a reference referencing an object. opposed to the common conception, std::move won't do any moving by itself, only casts the object to r-value-reference for the move constructor/assg. operator to kick in.
here, the pointer still holds valid data since it was not moved, only casted to r-value-reference.
if you want the object to move you have to declare the parameter as object, not reference : f(std::unique_ptr<T> uptr)
In your edit, you have undefiend behaviour, so everything may occure.
The reason why your call to show doesn't crash is because it doesn't use the this pointer (it doesn't try to modify or access a data member).
Try this:
class T{
public:
int v;
T(){}
void show(){
v = 0;
std::cout << "HEJ!\n";
}
};
void f(std::unique_ptr&& ref)
This is the answer when you initially had your f function taking a rvalue reference &&.
Your function takes a rvalue reference. Therefore, no new unique_ptr object is created yet, you are simply passing a reference.
Inside your f function, if you create a a local unique_ptr, with the parameter uptr, then finally uptr will be moved to create that new object.
template<typename T>
void f(std::unique_ptr<T>&& uptr)
{
//move uptr into local_unique_ptr
//note that we have to use move again
//because uptr has a name, therefore its a lvalue.
auto local_unique_ptr = std::unique_ptr<T>(std::move(uptr));
}
The important thing to always know is that std::move is simply a static_cast.
If you pass a lvalue to std::move, it returns a rvalue. If you pass a rvalue, it returns a rvalue. That's it.
Your function f may not in fact move the pointer. Merely taking an object by && does not modify the object.
u_ptr->do_sth() may invoke a static member function or a member function that does not access the object (this) and this is why it does not crash.
I have C++ code that when I compile it I get the following error message:
error: no matching function for call to ‘DataSourceScheme::initObject(const QString&, const QString&, QVector<ColumnScheme*>* const&)’
initObject(datasourcescheme.name_, datasourcescheme.cmd_, datasourcescheme.columns_);
note: no known conversion for argument 3 from ‘QVector<ColumnScheme*>* const’ to ‘const QVector<const ColumnScheme*>*
The C++ code:
DataSourceScheme::DataSourceScheme(const DataSourceScheme &datasourcescheme) {
initObject(datasourcescheme.name_, datasourcescheme.cmd_, datasourcescheme.columns_);
}
void DataSourceScheme::initObject(const QString &name, const QString &cmd, const QVector<const ColumnScheme*> *columns) {
name_ = name;
cmd_ = cmd;
columns_ = new QVector<ColumnScheme*>();
if (columns != NULL) {
for (const ColumnScheme *column : *columns) {
addColumn(*column);
}
}
}
Please help
Thanks!
Perhaps you need clarification on the usage of const. foo* const A declares an object of type foo and creates a constant pointer to that object. The object can be changed but not the pointer. const foo* creates a pointer to a constant object. The object foo is not modifiable.
You are trying to pass a constant pointer to a list of editable objects to a function which requires a pointer to a list of constant objects. Do you see where this leads to problems? The usage within the function does not match the criteria set at the creation of the objects being passed.
You can pass a normal pointer to a function requiring a constant pointer. This conversion is OK, i.e.
void func(foo* const param); // Function declaration
foo* A;
func(A); // Function call OK
Also,
void func(foo* const param); // Function declaration
foo* const A;
func(A); // Function call OK
You cannot pass a pointer to a constant object to a function requiring a normal pointer. This conversion is OK, i.e.
void func(foo* param); // Function declaration
const foo* A;
func(A); // Error
I will add a side note. The use of void func(foo* const param); is good practice IMHO to signify that the function func is not expected to delete, manage memory, or reassign the pointer. This is why many microsoft API calls use the const keyword. They merely use the object but in no way manage it.
I don't understand why this is allowed:
void Renderer::UpdateTextureFromArray(unsigned int* colors, unsigned int size, TextureData* textureData) const
{
D3D11_MAPPED_SUBRESOURCE ms;
this->deviceContext->Map(textureData->texture, 0, D3D11_MAP_WRITE_DISCARD, NULL, &ms);
memcpy(ms.pData, colors, sizeof(unsigned int) * size * size);
this->deviceContext->Unmap(textureData->texture, 0);
}
I made the UpdateTextureFromArray function const, yet I'm still allowed to call a non-const function on its members?
In this case, is it bad style for me to label the function as const?
EDIT: To clarify, is it "lying" to society if I have a function like this const? In a perfect world, this code wouldn't compile, right?
Presumably deviceContext is a pointer data member, so a const method cannot modify the pointer. But is is allowed to modify the object the pointer points to:
struct Bar {
void bar() {} // non const method
};
struct Foo {
Foo() : p(0) {}
void foo() const { p->bar();} // const method calling non-const method of Bar
Bar* p;
};
int main()
{
const Foo f;
f.foo(); // OK, Foo::p is not modified
}
You're not calling a non-const function on its member, you're dereferencing its member (which doesn't modify it, so is allowed to be done with a const pointer) and then calling a non-const member on the result of that dereference.
You are not calling non-const function on its members, you are accessing a pointer (which is const) which is pointing to a non-const object. You are thereby able to call non-const functions on it.
Regarding style, a const method is a method that doesn't change the state of the object from the users viewpoint. So you have to consider yourself if this pointer access does this or not. Some classes are candidates for parallelization in which case const methods are regarded as safe methods to parallelize as they are supposed to be without side effects.
So to qualify a method for const, I would suggest that it:
Isn't a candidate for parallelization and have no user visible side effects
Is a candidate for parallelization but have proper synchronization or no side effects at all.