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?
is_nothrow_assignable<T, U>tells you, givenwhether
f() = g()is valid and non-throwing.Due to reference collapsing, when
Tis a reference,T&&is the same asT. Otherwise,T&&is "rvalue reference toT", as you may have expected.So, for some object types
A,B,is_nothrow_assignable<A, B>tells you whether an rvalue of typeAcan be assigned from an rvalue of typeBwithout potentially throwing an exception. This is hardly interesting, because you rarely need to assign to an rvalue.is_nothrow_assignable<A&&, B&&>is equivalent tois_nothrow_assignable<A, B>.is_nothrow_assignable<A&, const B&>tells you whether an lvalue of typeAcan be assigned from a const lvalue of typeBwithout potentially throwing an exception.is_nothrow_assignable<A&, B>tells you whether an lvalue of typeAcan be assigned from an rvalue of typeBwithout potentially throwing an exception.is_nothrow_assignable<A&, B&&>is equivalent tois_nothrow_assignable<A&, B>.