std::swap is declared this way:
template <class T> void swap (T& a, T& b)
noexcept (is_nothrow_move_constructible<T>::value &&
is_nothrow_move_assignable<T>::value);
If I disable exceptions in my program (like with -fno-exceptions for g++) will std::swap use move operations for my custom types if they are move-enabled no matter if they are noexcept or not?
EDIT: follow-up question:
After realizing that std::swap will always use moves if my type has them, my real question is what happens to traits like is_nothrow_move_assignable<>?
Will std::vector always use moves when reallocating if my types have noexcept(true) move operations?
The noexcept-specification on swap solely tells the user where she can use swap without encountering an exception. The implementation is practically always equivalent to
auto tmp = std::move(a);
a = std::move(b);
b = std::move(tmp);
Which moves the objects around if, and only if, overload resolution selects a move assignment operator and/or constructor.
Yes. noexcept simply specifies that std::swap will not throw if T's move constructor and move assignment will not throw. It does not affect the behavior of the body of swap whatsoever - which will use T's move constructor and move assignment regardless of whether or not they throw and regardless of whether or not you compile with exceptions enabled.
Related
I have a couple of questions about move assignment operator and possibly thrown exceptions:
Provided that is_nothrow_move_assignable<T>::value is true for the type T, is it enough to conclude that move assignment operator for that type will never throw an exception? In other words, is it ok to have this noexcept specification:
struct MyClass
{
MyClass& operator = (MyClass&& other) noexcept(std::is_nothrow_move_assignable<OtherClass>::value)
{
this->data = std::move(other.data);
return *this;
}
OtherClass data;
};
(Let's assume that we cannot rely on defaulted move-assignment operator)
Talking specifically about std::vector, in C++11 its move-assignment operator may throw implementation-defined exceptions (according to the docs). And even since C++17 its noexcept specification still depends on the allocator traits. But this is a general case. Let's say we have two instrances of std::vector<double> with default allocator. Is it still possible that move assignment throws an exception in this case?
std::vector<double> v1{ 1,2,3 }, v2{4,5};
v1 = std::move(v2); // can it throw?
No.noexcept keyword says only, that it does not suppose to throw any exception and compiler will assume that the function will not throw exception, thus it will not produce exception-handing code. If noexcept function will throw an exception, the program will be terminated immediately. Thus said, I believe, using noexcept(std::is_nothrow_move_assignable<OtherClass>::value) is enough.
Lets take a look at the standard and cppreference:
https://en.cppreference.com/w/cpp/container/vector/operator%3D :
noexcept(std::allocator_traits<Allocator>::propagate_on_container_move_assignment::value
|| std::allocator_traits<Allocator>::is_always_equal::value)
std::allocator_traits<Allocator>::propagate_on_container_move_assignment::value, standard says([allocator.requirements])
If X::propagate_on_-
container_move_assignment::value is true, X shall satisfy the MoveAssignable requirement and the move operation shall not throw exceptions.
the double type is plain old builtin type, so it satisfies MoveAssignable requirements (https://en.cppreference.com/w/cpp/named_req/MoveAssignable) and thus exception should not be thrown.
std::allocator_traits<Allocator>::is_always_equal::value is true
In noexcept statement binary OR is used, so it shall not throw any exceptions.
I just noticing a std::vector<Foo> of mine was copying instead of moving its elements when resizing - even though Foo has a move ctor:
class Foo {
// ...
Foo(Foo&& other) : id_(other.id_), ptr_(other.ptr_), flag(other.flag)
{
other.flag = false;
};
// ...
int id_;
void* ptr_;
bool flag;
}
Then I read:
Resize on std::vector does not call move constructor
which reminded me that std::vector will only use move construction if the elements' move ctor is declared noexcept. When I add noexcept, move ctor's are called.
My question is: Why, given the move ctor's code, does the compiler not determine it to be noexcept? I mean, it can know for a fact that an exception cannot be thrown. Also, is inferring noexcept disallowed by the standard, or not just done by my specific compiler?
I'm using GCC 5.4.0 on GNU/Linux.
tl;dr: The compiler is not allowed to infer noexcept
Why, given the move ctor's code, does the compiler not determine it to be noexcept?
Because noexcept specification is determined based on the declaration - not the definition. This is analogous to how const specification works. The compiler isn't allowed to determine a function to be const even though its implementation doesn't modify any members.
is inferring noexcept disallowed by the standard
As I understand, yes:
[except.spec] ... absence of an exception-specification in a
function declarator other than that for a destructor (12.4) or a deallocation function (3.7.4.2) denotes an
exception specification that is the set of all types.
Inferring something other than set of all types would contradict this rule. Of course, when the compiler can prove that no exception can be thrown, it can optimise away any stack unwinding code under the as-if rule, but such optimisations cannot affect SFINAE introspection.
There has been discussion about possibility of introducing noexcept(auto), which would be an explicit way of letting the compiler infer noexcept specification.
I would like to use the optional idiom inside my constexpr function to easily clarify if the variable is set or not.
What I have tried with std::experimental::optional:
constexpr bool call()
{
std::experimental::optional<bool> r;
r = true; // Error
// Similar error with:
// r = std::experimental::optional<bool>(true);
if (!r)
{
return false;
}
return *r;
}
I get the error: call to non-constexpr function - so the assignment is not possible, because this operation cannot be constexpr (Example).
But if I implement my own (very ugly, just for example) optional class, it works, because I don´t implement the assignment operator/constructor explicit.
template<typename T>
struct optional
{
bool m_Set;
T m_Data;
constexpr optional() :
m_Set(false), m_Data{}
{
}
constexpr optional(T p_Data) :
m_Set(true), m_Data(p_Data)
{
}
explicit constexpr operator bool()
{
return m_Set;
}
constexpr T operator *()
{
return m_Data;
}
};
How could I use std::..::optional in the same context with assignment inside constexpr functions?
Basically, you can't. The problem with your simple implementation is that it requires T be default-constructible - if this is not the case, this won't work.
To get around this, most implementation use either a union or some (suitably aligned) storage that can hold a T. If you are passed a T in the constructor, then all well and good, you can initialize this directly (hence it will be constexpr). However, the tradeoff here is that when calling operator=, copying the value across may require a placement-new call, which cannot be constexpr.
For example, from LLVM:
template <class _Up,
class = typename enable_if
<
is_same<typename remove_reference<_Up>::type, value_type>::value &&
is_constructible<value_type, _Up>::value &&
is_assignable<value_type&, _Up>::value
>::type
>
_LIBCPP_INLINE_VISIBILITY
optional&
operator=(_Up&& __v)
{
if (this->__engaged_)
this->__val_ = _VSTD::forward<_Up>(__v);
else
{
// Problem line is below - not engaged -> need to call
// placement new with the value passed in.
::new(_VSTD::addressof(this->__val_)) value_type(_VSTD::forward<_Up>(__v));
this->__engaged_ = true;
}
return *this;
}
As for why placement new is not constexpr, see here.
This is not possible, as explained in n3527:
Making optional a literal type
We propose that optional<T> be a literal type for trivially
destructible T's.
constexpr optional<int> oi{5};
static_assert(oi, ""); // ok
static_assert(oi != nullopt, ""); // ok
static_assert(oi == oi, ""); // ok
int array[*oi]; // ok: array of size 5
Making optional<T> a literal-type in general is impossible: the
destructor cannot be trivial because it has to execute an operation
that can be conceptually described as:
~optional() {
if (is_engaged()) destroy_contained_value();
}
It is still possible to make the destructor trivial for T's which
provide a trivial destructor themselves, and we know an efficient
implementation of such optional<T> with compile-time interface —
except for copy constructor and move constructor — is possible.
Therefore we propose that for trivially destructible T's all
optional<T>'s constructors, except for move and copy constructors,
as well as observer functions are constexpr. The sketch of reference
implementation is provided in this proposal.
In other words, it's not possible to assign a value to r even if you mark it as constexpr. You must initialize it in the same line.
How could I use std::..::optional in the same context with assignment
inside constexpr functions?
std::optional is meant to hold a value that may or may not be present. The problem with std::optional's assignment is that it must destroy the old state (call the destructor of the contained object) if any. And you cannot have a constexpr destructor.
Of cause, Trivial and integral types shouldn't have a problem, but I presume the generalization was to keep things sane. However, Assignment could have been made constexpr for trivial types. Hopefully, it will be corrected. Before then, you can role out yours. :-)
Even std::optional's constructor that you think is constexpr, is actually selectively constexpr (depending on whether the selected object constructor is). Its proposal can be found here
Unfortunately, constexpr support in std::optional is somewhat rudimentary; the constexpr-enabled member functions are just the (empty and engaged) constructors, the destructor and some observers, so you cannot alter the engaged state of an optional.
This is because there would be no way to implement assignment for non-trivially copyable types without using placement new and in-place destruction of the contained object, which is illegal within constexpr context. The same currently holds for copy and move constructors, although that may change with guaranteed copy elision, but in any case the standard marks those special member functions as non-constexpr, so you cannot use them in constexpr context.
The fix would be to make the assignment operator conditionally constexpr dependent on whether the contained type is trivial (std::is_trivial_v<T>).
There is some discussion of this issue at the reference implementation; although it's probably too late to get constexpr assignment for trivial optionals into the next version of the Standard, there's nothing preventing you writing your own (e.g. by copying and fixing the reference implementation).
. or even allowed by the C++11 standard?
And if so, is there any compiler that actually does it?
Here is an example of what I mean:
template<class T> //T is a builtin type
class data
{
public:
constexpr
data() noexcept :
x_{0,0,0,0}
{}
constexpr
data(const T& a, const T& b, const T& c, const T& d) noexcept :
x_{a,b,c,d}
{}
data(const data&) noexcept = default;
data& operator = (const data&) noexcept = default;
constexpr const T&
operator[] (std::size_t i) const noexcept {
return x_[i];
}
T&
operator[] (std::size_t i) noexcept {
return x_[i];
}
private:
T x_[4];
};
template<class Ostream, class T>
Ostream& operator << (Ostream& os, const data<T>& d)
{
return (os << d[0] <<' '<< d[1] <<' '<< d[2] <<' '<< d[3]);
}
template<class T>
inline constexpr
data<T>
get_data(const T& x, const T& y)
{
return data<T>{x + y, x * y, x*x, y*y};
}
int main()
{
double x, y;
std::cin >> x >> y;
auto d = data<double>{x, y, 2*x, 2*y};
std::cout << d << std::endl;
//THE QUESTION IS ABOUT THIS LINE
d = get_data(x,y);
d[0] += d[2];
d[1] += d[3];
d[2] *= d[3];
std::cout << d << std::endl;
return 0;
}
Regarding the marked line:
Could the values x+y, x*y, x*x, y*y be written directly to the memory of d?
Or could the return type of get_data be directly constructed in the memory of d?
I can't think of a reason to not allow such an optimization. At least not for a class that has only constexpr constructors and default copy and assignment operators.
g++ 4.7.2 elides all copy constructors in this example; it seems however that assignment is always performed (even for default assignment only - as far as I can tell from the assembly that g++ emits).
The motivation for my question is the following situation in which such an optimization would greatly simplify and improve library design.
Suppose you write performance-critical library routines using a literal class. Objects of that class will hold enough data (say 20 doubles) that copies have to be kept to a minimum.
class Literal{ constexpr Literal(...): {...} {} ...};
//nice: allows RVO and is guaranteed to not have any side effects
constexpr Literal get_random_literal(RandomEngine&) {return Literal{....}; }
//not favorable in my opinion: possible non-obvious side-effects, code duplication
//would be superfluous if said optimization were performed
void set_literal_random(RandomEngine&, Literal&) {...}
It would make for a much cleaner (functional programming style) design if I could do without the second function. But sometimes I just need to modify a long-lived Literal object and have to make sure that I don't create a new one and copy-assign it to the one I want to modify. The modification itself is cheap, the copies aren't - that's what my experiments indicate.
EDIT:
Let's suppose the optimization shall only be allowed for a class with noexcept constexpr constructors and noexcept default operator=.
Elision of default copy/move assignment operators is allowed based on the general as-if rule only. That is the compiler can do it if it can ascertain that it will have no observable effect on behaviour.
In practice the as-if rule is used in general fashion to allow optimizations at intermediate representation and assembly levels. If the compiler can inline the default constructor and assignment, it can optimize them. It won't ever use the code of the copy constructor for it, but for their default implementations it should end up with the same code.
Edit: I answered before there was the code sample. Copy/move constructors are elided based on explicit permission to the compiler to do so, therefore they are elided even if they have observable effect (printing "COPY"). Assignments can only be elided based on as-if rule, but they have observable effect (printing "ASSIGN"), so the compiler is not allowed to touch them.
Does the standard allow for the elision of the assignment operator? Not in the same way as for construction. If you have any construct d = ..., the assignment operator will be called. If ... results in an expression of the same type as d, then the appropriate copy or move assignment operator will be called.
It is theoretically possible that a trivial copy/move assignment operator could be elided. But implementations are not allowed to elide anything that you could detect being elided.
Note that this is different from actual copy/move elision, because there the standard explicitly allows the elision of any constructor, whether trivial or not. You can return a std::vector by value into a new variable, and the copy will be elided if the compiler supports it. Even though it is very easy to detect the elision. The standard gives special permission to compilers to do this.
No such permission is granted for copy/move assignment. So it could only ever "elide" something that you couldn't tell the difference about. And that's not really "elision"; that's just a compiler optimization.
Objects of that class will hold enough data (say 20 doubles) that copies have to be kept to a minimum.
There's nothing stopping you from returning that Literal type right now. You will get elision if you store the object in a new variable. And if you copy assign it to an existing variable, you won't. But that's no different from a function which returns a float that you store into an existing variable: you get a copy of the float.
So it's really up to you how much copying you want to do.
There is an important drawback to what you propose: what would happen if the constructor threw? The behaviour for that case is well defined in the standard (all data members that had already been constructed are destructed in reverse order), but how would that translate into the case where we are "constructing" the object into an already existing one?
Your example is easy because the constructor cannot throw when T = double, but this is not so in the general case. You might end up with a half destructed object and undefined behaviour would ensue, even if your constructor and assignment operator were well behaved.
Jan Hudec's answer has a very good point about the as-if rule (+1 for him).
Hence, the assignment elision is allowed -- as he said -- provided that there's no observable effect. The fact that your assignment operator outputs "ASSIGN" is enough to prevent the optimization.
Notice that the situation is different for copy/move constructors because the standard allows elision for copy/move constructor even if they have observable side effects (see 12.8/31).
Suppose I have a class that doesn't support memberwise copying so I don't want to preserve compiler-implemented copy-constructor and assignment operator. I also don't want to implement those because either
doing so takes extra effort and I don't need those operations in my class or
those operations won't make sense in my class
so I want to prohibit them. To do so I'll declare them private and provide no implementation:
class NonCopyable {
private:
NonCopyable( const NonCopyable& ); //not implemented anywhere
void operator=( const NonCopyable& ); //not implemented anywhere
};
Now I can select any return type for operator=() member function. Will it matter which return type I select?
No, the return type doesn't matter.†
The C++ standard does not impose any requirements on the return type for copy assignment special member functions that you declare yourself. It just needs to be an operator=() that acccepts "exactly one parameter of type X, X&, const X&, volatile X& or const volatile X&".†† Therefore, void operator=( const NonCopyable& ); is still a copy assignment operator (a user-declared one, to be specific).
Because you have in fact supplied your own copy assignment operator, it will surpress the generation of the default copy assignment operator. This forces all calls to NonCopyable's copy assignment operator to resolve to yours, causing any attempts to use the copy assignment operator to fail to compile since it's declared private.
class Foo : NonCopyable
{
};
int main()
{
Foo a;
Foo b;
// Compiler complains about `operator=(const NonCopyable&)`
// not accessible or something like that.
a = b;
}
And since I'll never be able to actually use it, it doesn't matter that it's not exactly the canonical copy assignment operator. If I tried to use the copy assignment operator it would result in a compiler error, which is exactly what you want.
† Of course, it does matter, stylistically speaking, if the copy assignment operator actually does something. Generally you want your operators to behave just like the built-in ones, so returning a X& is good practice when you're actually doing assignment.
†† C++ Standard: 12.8 Copying class objects [class.copy]
9 A user-declared copy assignment operator X::operator= is a non-static non-template member function of class X with exactly one
parameter of type X, X&, const X&, volatile X& or const
volatile X&.
No, since you'll never call this operator in your code. I tend to keep the return type NonCopyable&, for clarity and consistency.
It matters a tiny, tiny bit:
void ensures a small percentage of accidental/misguided calls (a = b = c / f(d = e)) from within the class's implementation produce compile time errors rather than link time errors, which may save compile time and be more understandable (minimally relevant for large classes touched by many developers, some with limited prior familiarity).
void would ring an alarm bell for me (and hopefully most developers), wondering whether you:
wanted to remove the default-generated operator=
were just lazy about the extra typing, or
were unfamiliar/uncaring re the generally expected semantics of operator=.
With the open question in mind, other programmer are less likely to come along and think you just didn't get around to providing the implementation and add it casually (you may feel comments are adequate).
returning a reference to type may make the overall function signature more instantaneously recognisable, or visually searching past a complicated type to find operator= could have the opposite effect - all in the eye (and mind) of the beholder....
No, because you can return anything from operator=, even if you define its implementation.
No, it does not matter, since you never implement a return statement. If you try to invoke the operator, the compiler won't be able to find an implementation (with any return type, so the return type is irrelevant).
Interestingly, boost::noncopyable's copy assignment operator is declared to return a const noncopyable&, but I guess that's just convention.