Why is std::move not [[nodiscard]] in C++20?
AFAIK P0600R1 is the only proposal for adding [[nodiscard]]
to the standard library that was applied to C++20. From that paper:
We suggest a conservative approach:
[...]
It should not be added when:
- [...]
- not using the return value makes no sense but doesn’t hurt and is usually not an error
- [...]
So, [[nodiscard]] should not signal bad code if this
- [...]
- doesn’t hurt and probably no state change was meant that doesn’t happen
So the reason is that the standard library uses a conservative approach and a more aggresive one is not yet proposed.
Why does std::forward_list::empty has [[nodiscard]] while std::forward_list::max_size doesn't?
The reason is two-part:
There is no way to confuse the query "what is the maximum size?" expressed as
.maximum_size()
with anything else, while you could confuse the query "is it empty?" expressed as.empty()
with the command "empty it!", which got the name.clear()
.[[nodiscard]]
is new, and has not been applied everywhere applicable in the standard library (to date). C++20 adds some places, but still isn't anywhere near comprehensive.
Why not apply [[nodiscard]] to every constructor?
An example from the pybind11
library: To wrap a C++-class for python, you do:
PYBIND11_MODULE(example, m) {
py::class_<MyClass>(m, "MyClass"); // <-- discarded.
}
Why does [[nodiscard]] only encourage compiler to issue a warning and does not require it?
The C++ standard specifies the behavior of a valid C++ program. In so doing, it also defines what "valid C++ program" means.
Diagnostics are only required for code which is ill-formed, code which is syntactically or semantically incorrect (and even then, there are some ill-formed circumstances that don't require diagnostics). Either the code is well-formed, or it is ill-formed and (usually) a diagnostic is displayed.
So the very idea of a "warning" is just not something the C++ standard recognizes, or is meant to recognize. Notice that even the "implementations are encouraged to issue a warning" statement is in a non-normative notation, rather than a legitimate specification of behavior.
Why is unique_ptr::release not defined with [[nodiscard]]?
This is addressed in the paper that added [[nodiscard]]
to many of the functions. From P0600R1 this is the remark about adding [[nodiscard]]
to unique_ptr::release()
Titus: at Google 3.5% of calls would fail, but analysis showed
that it was correct (but weird ownership semantics). See
reflector email.
Is nodiscard necessary on operators?
Let me cite the following paper by N.Josuttis: "[[nodiscard]]
in the library" (with some omissions, see the full paper):
C++17 introduced the
[[nodiscard]]
attribute. The question is, where to apply it now in the standard library. It should be added where:
- not using the return value always is a “huge mistake” (e.g. always resulting in resource leak),
- not using the return value is a source of trouble and easily can happen (not obvious that something is wrong).
It should not be added when:
- not using the return value is a possible/common way of programming at least for some input,
- not using the return value makes no sense but doesn’t hurt and is usually not an error.
So,
[[nodiscard]]
should not signal bad code if this
- can be useful not to use the return value,
- is common not to use the return value,
- doesn’t hurt and probably no state change was meant that doesn’t happen.
c++20: how to move capture a class instance
IMO, the reason is that the type lambda you passed to std::function
ctor violate std::function
type requirement, which needs the passed Callable
must be CopyConstructible
. Since the captured type A
's copy constructor is deleted, it results that the lambda's copy constructor cannot be implicitly-declared.
Here's some reference from cppreference page:
- Initializes the target with std::forward<F>(f). The target is of type std::decay<F>::type. If f is a null pointer to function, a null pointer to member, or an empty value of some std::function specialization, *this will be empty after the call. This constructor does not participate in overload resolution unless the target type is not same as function, and its lvalue is Callable for argument types Args... and return type R. The program is ill-formed if the target type is not copy-constructible or initialization of the target is ill-formed.
Also from the same page:
std::decay<F>::type must meet the requirements of Callable and CopyConstructible.
Possible solution 1
If c++23 is acceptable to you, you can directly use std::move_only_function
. https://godbolt.org/z/qhfd83955
Possible solution 2
You can write a custom naive function wrapper to handle your move only type, which is a simple type-erasure class with a function call operator, basically the same thing as chapter 9 of book "C++ Concurrency In Action":
class TaskWrapper {
struct impl_base {
virtual void call() = 0;
virtual ~impl_base() = default;
};
std::unique_ptr<impl_base> impl;
template <typename F>
struct impl_type : impl_base {
F f;
impl_type(F&& f_) : f(std::move(f_)) {}
void call() final { f(); }
};
public:
template <typename F>
TaskWrapper(F&& f) : impl(new impl_type<F>(std::forward<F>(f))) {}
void operator()() { impl->call(); }
TaskWrapper() = default;
TaskWrapper(TaskWrapper&& other) noexcept : impl(std::move(other.impl)) {}
TaskWrapper& operator=(TaskWrapper&& other) noexcept {
impl = std::move(other.impl);
return *this;
}
TaskWrapper(const TaskWrapper&) = delete;
TaskWrapper(TaskWrapper&) = delete;
TaskWrapper& operator=(const TaskWrapper&) = delete;
};
And change B
signature to:
class B {
public:
explicit B(TaskWrapper cb) { (void)(cb); };
};
Then use it like:
B b_factory() {
auto a { A() };
return B {
TaskWrapper([a = std::move(a)]() {
// do something with a
})
};
}
What is std::move(), and when should it be used?
Wikipedia Page on C++11 R-value references and move constructors
- In C++11, in addition to copy constructors, objects can have move constructors.
(And in addition to copy assignment operators, they have move assignment operators.) - The move constructor is used instead of the copy constructor, if the object has type "rvalue-reference" (
Type &&
). std::move()
is a cast that produces an rvalue-reference to an object, to enable moving from it.
It's a new C++ way to avoid copies. For example, using a move constructor, a std::vector
could just copy its internal pointer to data to the new object, leaving the moved object in an moved from state, therefore not copying all the data. This would be C++-valid.
Try googling for move semantics, rvalue, perfect forwarding.
Related Topics
Getting Actual File Name (With Proper Casing) on Windows
Read and Write on Serial Port in Ubuntu with C/C++ and Libserial
How to Get the Precision of High_Resolution_Clock
How to Set the Background Image in Qt Stylesheet
How Does "Using Std::Swap" Enable Adl
All K Nearest Neighbors in 2D, C++
Is It Okay to Use "And", "Or" etc. Instead of "&&", "||"
Allowing Access to Private Members
Running a Windows Program and Detect When It Ends with C++
How to Bit Shift a Long by More Than 32 Bits
Multithreading on Dual Core MAChine