I want to pass a raw pointer inside lambda, but I don't want it to be leaked, if the lambda isn't invoked. It looks like this:
void Clean(std::unique_ptr<int>&& list);
void f(int* list) {
thread_pool.Push([list = std::unique_ptr<int>(list) ] {
Clean(std::move(list)); // <-- here is an error.
});
}
I get an error in Clang 3.7.0:
error: binding of reference to type 'unique_ptr<[2 * ...]>' to a value of type 'unique_ptr<[2 * ...]>' drops qualifiers
But I don't see any qualifiers at the first place, especially dropped.
Also, I found similar report on the mailing list, but without answer.
How should I modify my code, so it gets compiled and works as expected by semantics?
You need to make the inner lambda mutable:
[this](Pointer* list) {
thread_pool.Push([this, list = std::unique_ptr<int>(list) ]() mutable {
^^^^^^^^^
Clean(std::move(list));
});
};
operator() on lambdas is const by default, so you cannot modify its members in that call. As such, the internal list behaves as if it were a const std::unique_ptr<int>. When you do the move cast, it gets converted to a const std::unique_ptr<int>&&. That's why you're getting the compile error about dropping qualifiers: you're trying to convert a const rvalue reference to a non-const rvalue reference. The error may not be as helpful as it could be, but it all boils down to: you can't move a const unique_ptr.
mutable fixes that - operator() is no longer const, so that issue no longer applies.
Note: if your Clean() took a unique_ptr<int> instead of a unique_ptr<int>&&, which makes more sense (as it's a more explicit, deterministic sink), then the error would have been a lot more obvious:
error: call to deleted constructor of `std::unique_ptr<int>`
note: 'unique_ptr' has been explicitly marked deleted here
unique_ptr(const unique_ptr&) = delete
^
Related
I'm trying to write a fairly simple method that returns a future. A lambda sets the future. This is a minimal example. In reality the lambda might be invoked in a different thread, etc.
#include <future>
std::future<std::error_code> do_something() {
std::promise<std::error_code> p;
auto fut = p.get_future();
auto lambda = [p = std::move(p)] {
std::error_code error;
p.set_value(error);
};
lambda();
return std::move(fut);
}
int main() { return do_something().get().value(); }
For some reason I get a type error. VSCode intellisense says:
no instance of overloaded function "std::promise<_Ty>::set_value [with _Ty=std::error_code]" matches the argument list and object (the object has type qualifiers that prevent a match) -- argument types are: (std::remove_reference_t<std::error_code &>) -- object type is: const std::remove_reference_t<std::promise<std::error_code> &>
And MSVC compiler says:
error C2663: 'std::promise<std::error_code>::set_value': 2 overloads have no legal conversion for 'this' pointer
I really don't understand the VS Code error. Is it saying that it thinks error is a const promise<error_code>? How do I correctly call set_value on a promise which was moved inside a lambda's capture?
By default lambda stores all its captured values (non-references) as const values, you can't modify them. But lambda supports keyword mutable, you can add it like this:
[/*...*/](/*...*/) mutable { /*...*/ }
This will allow inside body of a lambda to modify all its values.
If for some reason you can't use mutable, then you can use other work-around:
[/*...*/, p = std::make_shared<ClassName>(std::move(p)), /* ... */](/*...*/) {/*...*/}
In other words wrap your moved value into std::shared_ptr, you can also use std::unique_ptr if you like.
Wrapping into shared pointer solves the problem, because shared pointer (unique also) allows to modify its underlying object value even if pointer itself is const.
Don't forget inside the body of a lambda to dereference p as a pointer, in other words if you used p.SomeMethod(), now you have to use p->SomeMethod() (with -> operator).
if I have a function:
Foo& Bar()
{
return /// do something to create a non-temp Foo here and return a reference to it
}
why is this:
auto x = Bar(); /// probably calls copy ctor - haven't checked
not the same as this?
auto &x = Bar(); /// actually get a reference here
(Actually, I'd expect the second version to get a reference to a reference, which makes little sense.)
If I explicitly specified the type of x as a value or a reference, I'll get what I expect (of course). I would expect, though, that auto would compile to the return type of Bar(), which, in this case, is a reference.
Is there an implicit cast between Foo and Foo& that comes into play here?
(Spec references accepted, though I'm getting tired of reading committee-speak.)
(Second use of time machine will be making C++ pass by reference by default. With a #pragma compatibility trigger for compiling C code. ARGH.)
The type deduction for auto works exactly the same as for templates:
when you deduce auto you will get a value type.
when you deduce auto& you wil get a non-const reference type
when you deduce const auto& you will get a const reference
when you deduce auto&& you will get
a non-const reference if you assign a non-const reference
a const reference if you assign a const reference
a value when you assign a temporary
Taken directly from Herb Sutter's blog post:
auto means “take exactly the type on the right-hand side, but strip off top-level const/volatile and &/&&.”
I am just curious. I am passing pointer to function with signature
void printCommandReceived(const CommandDescriptor &descriptor)
as third parameter to constructor with signature
CommandLogFilter::CommandLogFilter(QSharedPointer<LogServer> logServer, QObject *parent,
void (*preprocessValidCommand)(CommandDescriptor &descriptor))
and getting error from g++ compiler:
error: invalid conversion from ‘void (*)(const CommandDescriptor&)’ to ‘void (*)(CommandDescriptor&)’ [-fpermissive]
In my understanding the reference to non-const object should be usable as argument to reference to const object parameter. So parameter with type pointer to function accepting non-const object reference should be more than satisfied with (and do implicit conversion from) argument of type pointer to function, which accepts even const object reference.
Where am I wrong?
void (*)(const CommandDescriptor&) and void (*)(CommandDescriptor&) are two completely different, unrelated types.
There are very simple rules regarding const: X* can be converted to X const*, X** can be converted to X const * const * and so on. Same thing with references. Nothing else is allowed.
Note that the rules do not allow to arbitrarily add or remove const at any position in the type, for example, X** cannot be converted to X const **. This is true also for the position of function arguments: you just cannot add or remove const there to get a compatible type.
Could these rules be extended so that they accommodate cases like yours and remain consistent? Probably so. But they are not.
C++ has a limited set of situations where const can be added or removed implicitly. You ran into one where it cannot be done. The reason why not is probably as simple as "describing those cases which are safe would be hard, and standards writers are lazy and conservative".
As a work around, you can do this:
CommandLogFilter bob(
logServer,
parent,
[](CommandDescriptor &descriptor) {
return printCommandReceived(descriptor);
}
);
as stateless lambdas can implicitly convert-to a pointer to a function matching their signature.
I don't like having to make the signature explicit there, but there is no way to do something similar with template "auto" lambdas and have the signature deduced unfortunately.
I was making a thin derived class with a forwarding constructor. (Bear with me, I must use GCC 4.7.2, which lacks inherited constructors).
On the first try, I forgot to add the explicit keyword and got an error. Could someone explain exactly why this particular error occurs? I'm having trouble figuring out the sequence of events.
#include <memory>
template<typename T>
struct shared_ptr : std::shared_ptr<T>
{
template<typename...Args>
/*explicit*/ shared_ptr(Args &&... args)
: std::shared_ptr<T>(std::forward<Args>(args)...)
{}
};
struct A {};
struct ConvertsToPtr
{
shared_ptr<A> ptr = shared_ptr<A>(new A());
operator shared_ptr<A> const &() const { return ptr; }
};
int main()
{
shared_ptr<A> ptr;
ptr = ConvertsToPtr(); // error here
return 0;
}
The error:
test.cpp: In function ‘int main()’:
test.cpp:28:23: error: ambiguous overload for ‘operator=’ in ‘ptr = ConvertsToPtr()’
test.cpp:28:23: note: candidates are:
test.cpp:9:8: note: shared_ptr<A>& shared_ptr<A>::operator=(const shared_ptr<A>&)
test.cpp:9:8: note: shared_ptr<A>& shared_ptr<A>::operator=(shared_ptr<A>&&)
This is also the case with g++ 4.8.4 with the following:
g++ -g -pedantic --std=c++11 -o test main.cpp
The VS2015 settings are all defaulted.
The problem is that the compiler tries to convert a temporary returned by ConvertsToPtr() to a shared_ptr object. When the compiler is used with explicit keyword, then this conversion never occurs using the constructor. However, while examining with gdb it appears that instead it is using the shared_ptr<A> const &() conversion function to match the appropriate Type. This conversion then returns a const shared_ptr & which has no ambiguity when invoking the assignment operator (this is also match the findings of wojciech Frohmberg).
However, if the explicit is omitted, then an object of shared_ptr is returned. this can be matched either to rvalue version of the assignment operator or the const lvalue version.
According to N4296, Table-11, then we have, after the construction with the conversion constructor, a rvalue of shared_ptr object. However the overload resolution finds two matches, which both ranks under Exact Match (the rvalue version is Identity matching while the other is under Qualification matching).
I did check also on VS2015 and like stated in the comments, it works. But using some cout debugging one can see that the const lvalue assignment rvalue is prioritized over the rvalue const lvalue refrence version counterpart.
EDIT: I looked a little deeper in the standard and add the modification. the deleted text regarding the results VS2015 was wrong, because I didn't define both assignments. When both of assignments were declared it does prefer the rvalue.
I assume that the VS compiler distinct the Identity from the Qualification matching in ranking. However as I conclude, it is the VS compiler that is buggy. the g++ compilers obeys the given standard. However since GCC 5.0 Does work as Visual studio, The possibility of compiler bug is slim, so I would be happy to see another experts insights.
EDIT: In 13.3.3.2 one of the draw breakers, after the better ranking I wrote about it, is:
— S1 and S2 are reference bindings (8.5.3) and neither refers to an
implicit object parameter of a non-static member function declared
without a ref-qualifier, and S1 binds an rvalue reference to an rvalue
and S2 binds an lvalue reference.
There is an example attached showing that a given rvalue (not rvalue reference) is supposed to match a const int && over const int &. Therefore I guess, it is safe to assume that it is relevant to our case, even if we have && type and not const && type. I guess after all that GCC 4.7,4.8 is buggy after all.
I want to pass a raw pointer inside lambda, but I don't want it to be leaked, if the lambda isn't invoked. It looks like this:
void Clean(std::unique_ptr<int>&& list);
void f(int* list) {
thread_pool.Push([list = std::unique_ptr<int>(list) ] {
Clean(std::move(list)); // <-- here is an error.
});
}
I get an error in Clang 3.7.0:
error: binding of reference to type 'unique_ptr<[2 * ...]>' to a value of type 'unique_ptr<[2 * ...]>' drops qualifiers
But I don't see any qualifiers at the first place, especially dropped.
Also, I found similar report on the mailing list, but without answer.
How should I modify my code, so it gets compiled and works as expected by semantics?
You need to make the inner lambda mutable:
[this](Pointer* list) {
thread_pool.Push([this, list = std::unique_ptr<int>(list) ]() mutable {
^^^^^^^^^
Clean(std::move(list));
});
};
operator() on lambdas is const by default, so you cannot modify its members in that call. As such, the internal list behaves as if it were a const std::unique_ptr<int>. When you do the move cast, it gets converted to a const std::unique_ptr<int>&&. That's why you're getting the compile error about dropping qualifiers: you're trying to convert a const rvalue reference to a non-const rvalue reference. The error may not be as helpful as it could be, but it all boils down to: you can't move a const unique_ptr.
mutable fixes that - operator() is no longer const, so that issue no longer applies.
Note: if your Clean() took a unique_ptr<int> instead of a unique_ptr<int>&&, which makes more sense (as it's a more explicit, deterministic sink), then the error would have been a lot more obvious:
error: call to deleted constructor of `std::unique_ptr<int>`
note: 'unique_ptr' has been explicitly marked deleted here
unique_ptr(const unique_ptr&) = delete
^