Reputation: 139
I'm studying the functionality of the noexcept
specifier and the noexcept
operator.
I know that instead of using the noexcept
operator, there are some type traits that are an alternative to use like:
- is_nothrow_constructible
- is_nothrow_default_constructible
- is_nothrow_move_constructible
- is_nothrow_copy_constructible
- is_nothrow_assignable
- is_nothrow_move_assignable
- is_nothrow_copy_assignable
- is_nothrow_destructible
For example, in the case of the is_nothrow_assignable
and is_nothrow_constructible
.
struct A { };
struct B {
B& operator= (const A&) noexcept {return *this;}
B& operator= (const B&) {return *this;}
};
int main() {
std::cout << std::boolalpha;
std::cout << "is_nothrow_assignable:" << std::endl;
std::cout << "A=A: " << std::is_nothrow_assignable<A,A>::value << std::endl;
std::cout << "B=A: " << std::is_nothrow_assignable<B,A>::value << std::endl;
std::cout << "B=B: " << std::is_nothrow_assignable<B,B>::value << std::endl;
return 0;
}
In the code above, it is clear that it tests if one type can be non-throw assignable like std::is_nothrow_assignable<B, A>
.
But, I see in some templates of the C++ standard library that they pass the arguments with const
and/or &/&&
and some others don't use that, something like this definition of the std::exchange
:
_EXPORT_STD template <class _Ty, class _Other = _Ty>
_CONSTEXPR20 _Ty exchange(_Ty& _Val, _Other&& _New_val) noexcept(
conjunction_v<is_nothrow_move_constructible<_Ty>, is_nothrow_assignable<_Ty&, _Other>>) { // HERE
// assign _New_val to _Val, return previous _Val
_Ty _Old_val = static_cast<_Ty&&>(_Val);
_Val = static_cast<_Other&&>(_New_val);
return _Old_val;
}
The parameter types of std::exchange
are: _Ty&, _Other&&
.
In the third line of the function uses the is_nothrow_assignable
as:
is_nothrow_assignable<_Ty&, _Other>
// ^ ^
// | |
// Here is passing as reference |
// But not here
And in other functions is something similar:
//const& ----------------
// | |
pair(const T1& x, const T2& y) noexcept(
is_nothrow_constructible<T1, const T1&>::value &&
is_nothrow_constructible<T2, const T2&>::value );
// ^ ^ ^
// | | |
// Here without const or & ------ | |
// | |
// But here is with const and & -------------
My question is why they use const
and or &/&&
in some arguments and in others don't?
And how can I determine when/where to use them?
Upvotes: 4
Views: 154
Reputation: 981
is_nothrow_assignable<T, U>
tells you, given
T&& f();
U&& g();
whether f() = g()
is valid and non-throwing.
Due to reference collapsing, when T
is a reference, T&&
is the same as T
. Otherwise, T&&
is "rvalue reference to T
", as you may have expected.
So, for some object types A
, B
,
is_nothrow_assignable<A, B>
tells you whether an rvalue of type A
can be assigned from an rvalue of type B
without potentially throwing an exception. This is hardly interesting, because you rarely need to assign to an rvalue.is_nothrow_assignable<A&&, B&&>
is equivalent to is_nothrow_assignable<A, B>
.is_nothrow_assignable<A&, const B&>
tells you whether an lvalue of type A
can be assigned from a const lvalue of type B
without potentially throwing an exception.is_nothrow_assignable<A&, B>
tells you whether an lvalue of type A
can be assigned from an rvalue of type B
without potentially throwing an exception.is_nothrow_assignable<A&, B&&>
is equivalent to is_nothrow_assignable<A&, B>
.Upvotes: 2