Is noexcept specifier always necessary for mov constructor? - c++11

I'm reading some libraries for my project.
Most of them specify "noexcept" in any move constructor.
Is it necessary to write "noexcept" in move constructors or is it occasionally just happened in my reading codes?
Thank you.

I think this explains the reason why you see noexcept in move constructors:
"A class provides the Strong Exception Guarantee if after an exception occurs, the objects maintain their original values. The move members of a class explicitly change the state of their argument. Should an exception be thrown after some members have been moved, then the Strong Exception Guarantee may no longer hold as the from object has been modified. It is especially important to use noexcept for types that are intended to be used with the standard library containers. If the move constructor for an element type in a container is not noexcept then the container will use the copy constructor rather than the move constructor."
So to answer your question, you should use noexcept in every case where your constructor could throw an exception. If you don't use one and if an exception occurs, the Strong Exception Guarantee will fail which could cause problems in the error recovery of your code.
Source: http://www.codingstandard.com/rule/12-5-4-declare-noexcept-the-move-constructor-and-move-assignment-operator/

Related

Why the constructor of std::mutex in C++ does not throw?

The pthread_mutex_init() function returns a non-zero value when it fails to initialize the mutex, while the std::mutex class in C++11 has a constructor of noexcept.
Say one chooses to implement a C++ mutex class on top of pthreads mutex. He wraps a pthread mutex inside the class and tries to initialize it by calling pthread_mutex_init() in constructor. If the function call returns a value other than zero, meaning error, the error can't be reported immediately since the constructor can not throw. One alternative is to throw an exception until the lock method is actually called on the mutex. But this approach just seems wrong.
Is there another way to do this, employing some clever tricks to guarantee that initializing a mutex always succeed?
Update: I am going to answer my own question on this one. According to language standard, in 30.4.1.3 pge 1163, it says ". If initialization of an object of a mutex type fails, an exception of type system_error shall be thrown. "
And a function of noexcept can throw inside the function body, it is just the caller can not catch the exception. If an exception is thrown inside a noexcept function, std::terminate will be called.
The constructor of std::mutex needs to be constexpr (so that a global std::mutex can be statically initialized and used in constructors of other global objects), and therefore cannot call pthread_mutex_init (or similar functions) at all.
Instead, it needs to use PTHREAD_MUTEX_INITIALIZER or equivalent (e.g., SRWLOCK_INIT on Windows) to statically initialize the mutex.
It seems to me that errors from pthread_mutex_init are simply ignored in libstdc++:
https://github.com/psp2sdk/libs/blob/master/include/c%2B%2B/bits/gthr-posix.h#L732
where __gthread_mutex_init_function is via macro __GTHREAD_MUTEX_INIT_FUNCTION invoked here
https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/include/bits/std_mutex.h#L75
that is in std::mutex constructor via its base class.
UPDATE
One can initialize Pthread mutex with PTHREAD_MUTEX_INITIALIZER and then
no error checks are performed
I guess error handling can be postponed to locking functions; quoting from documentation of pthread_mutex_lock and pthread_mutex_trylock ERRORS section:
EINVAL The value specified by mutex does not refer to an initialized mutex object.
This implies that errors in pthread_mutex_init can be safely ignored in std::mutex constructor.
According to C++17 specification:
33.4.3.2 Mutex types [thread.mutex.requirements.mutex]
The mutex types shall be DefaultConstructible and Destructible. If initialization of an object of a mutex type fails, an exception of type system_error shall be thrown. The mutex types shall not be copyable or movable.
So mutex type may throw an exception not std::mutex. std::mutex has noexcept, but std::recursive_mutex does not and they are both mutex types:
33.4.3.2.1 Class mutex [thread.mutex.class]
constexpr mutex() noexcept;
33.4.3.2.2 Class recursive_mutex [thread.mutex.recursive]
recursive_mutex();
Moreover:
20.5.5.12 Restrictions on exception handling [res.on.exception.handling]
Any of the functions defined in the C++ standard library can report a failure by throwing an exception of a type described in its Throws: paragraph, or of a type derived from a type named in the Throws: paragraph that would be caught by an exception handler for the base type.
Functions defined in the C++ standard library that do not have a Throws: paragraph but do have a potentially throwing exception specification may throw implementation-defined exceptions. Implementations should report errors by throwing exceptions of or derived from the standard exception classes (21.6.3.1, 21.8, 22.2).
There is no Throws paragraph and it does not have potentially throwing exception specification.
So std::mutex constructor shall never throw an exception or call std::terminate the same way as any other function from standard library with noexcept specification on conforming C++ implementation.
If you sum it all up this leads to the fact that pthread_mutex_init cannot be called in the std::mutex constructor. There does not need to be a one to one mapping for the construction/initialization. On the contrary!

move semantics with temps allocated with new

I'm just wondering if move semantics are restricted to syntax style B.
More specifically, with style B the object is created on the stack and moved. With style A, the object is created on the heap, but it seems can't be moved.
The question very specifically is, can you use move semantics such that the temp is created with NEW? If so, how?
//move c'tor
A(A&& other) : num(other.num), s(other.s){
other.num = 0;
other.s = nullptr; //dyn alloc obj
}
If you do this, it doesn't work (syntax style A).
A a2(new A("blah")); //error
A a2(move(new A("blah"))); //error
This is ok (syntax style B)
A a2(A("blah")); //uses c'tor once
A a2(move(A("blah"))) //uses c'tor followed by move c'tor
You seem to be confused about a number of things, so I'll try to shed some light. You probably know most of this already.
Move semantics were designed to allow objects to transfer ownership of their data to another object. This was motivated largely to avoid copying from temporary objects that didn't need it.
The move constructor is much like the copy constructor, A(const A&), but only accepts a non-const xvalue of the same type, A(A&&) (think of it as an expiring value). Therefore, this constructor can be invoked when given something like a return value from a function or a variable moved via std::move.
Now, the function std::move in itself is a bit of a misnomer, it doesn't actually do anything. All it does it cast a T or T& into a T&&.
To address your question directly, nothing I've mentioned is specific to where the moving object is located, but it is specific about the types. A("blah") calls the constructor with automatic memory and returns that A. new A("blah") on the other-hand calls the constructor with dynamic memory and returns a pointer to that A, ergo an A*. So your syntax A is not trying to invoke the move constructor, but another constructor like A(A*).
To move from an object referenced by a pointer, all you need to do is derefence and move:
A* a1 = new A("blah");
A a2(std::move(*a1));
There's nothing stopping you from defining a constructor like A(A*), but that shouldn't be needed and isn't recommended for using move semantics.
Also, your syntax B comments are incorrect; both are a value-constructor A("blah") followed by the move-constructor A(A&&); the std::move doesn't add anything here.

interface function getting rvalue pointers to initialize shared_ptr

I have a class exposing through it's interface add function:
void AddObject(Object *o);
Inside the class I maintain the objects in set<shared_ptr<Object>>.
Since I will create shared_ptr from the received pointer I thought to limit the function argument to only rvalue pointers so to make sure that the user will not delete the pointer I use. And so I'll change the function declaration to:
void AddObject(Object* &&o);
so a typical use will be:
AddObject(new Object())
preventing the user to accidentally delete pointer I hold.
I don't want to to use shared_ptr in the interface because the user is not familiar with shared_ptr.
Do you think my suggestion is a good idea?
I think this is a bad idea. I'm sure there is a reason why shared_ptr c-tor that gets a raw pointer is marked explicit instead of using r-value. In my eyes, It's better to teach the users once about smart pointers or at least teach them about using make_shared/make_unique (which are safer and, in the case of make_shared, more efficient, BTW).
BTW, why shared_ptr and not unique_ptr?
Also, why set? Even if you want to make sure you hold each pointer only once and searching a vector each time doesn't look natural enough in your code, I don't see a reason to hold the pointers sorted instead of using unordered_set.
First of all, this approach will not prevent the user from deleting the pointer. Consider this example
auto obj = new Object();
AddObject(std::move(obj));
delete obj;
Secondly, the amount of steps between calling new and the creation of shared_ptr should be as few as possible. If anything happens inside AddObject before it can create a shared_ptr, the object will never get deleted.
The same applies if there are more arguments to AddObject(). If constructing those fails, you will leak memory.
void AddObject(Object* &&o, SomeOtherObject* x);
AddObject(new Object(), xx()); // if xx() throws, memory leak will occur
Ideally you would "wrap" object creating into shared_ptr construction:
void AddObject(std::shared_ptr<Object> o);
AddObject(std::make_shared<Object>());
Either of the following methods may solve your problem.
You may append more comments for AddObject to tell users that delete the pointer they added is not allowed. This is almost enough.
Or, you could also make Object inherits from a base class which has a private destructor and a method named destroyByOwner.

Move semantics in dependency injection

Is it possible or helpful to employ move semantics when preforming constructor injection?
If so, is Hypodermic setup to allow this?
Hypodermic can automatically use move constructors, it can spare you the cost of updating the reference counter of your dependencies.
You have nothing special to do but provinding a move constructor of the concerned object.
For now, Hypodermic cannot resolve an object providing both version of its constructor (move vs non-move), that is, the compiler will complain about ambigous call.

State of object after std::move construction

Is it legal/proper c++0x to leave an object moved for the purpose of move-construction in a state that can only be destroyed? For instance:
class move_constructible {...};
int main()
{
move_constructible x;
move_constructible y(std::move(x));
// From now on, x can only be destroyed. Any other method will result
// in a fatal error.
}
For the record, I'm trying to wrap in a c++ class a c struct with a pointer member which is always supposed to be pointing to some allocated memory area. All the c library API relies on this assumption. But this requirement prevents to write a truly cheap move constructor, since in order for x to remain a valid object after the move it will need its own allocated memory area. I've written the destructor in such a way that it will first check for NULL pointer before calling the corresponding cleanup function from the c API, so that at least the struct can be safely destroyed after the move.
Yes, the language allows this. In fact it was one of the purposes of move semantics. It is however your responsibility to ensure that no other methods get called and/or provide proper diagnostics. Note, usually you can also use at least the assignment operator to "revive" your variable, such as in the classical example of swapping two values.
See also this question

Resources