user11611653
user11611653

Reputation: 139

What is the correct form of passing parameters to std::is_nothrow_assignable -like functions in C++?

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

Answers (1)

cpplearner
cpplearner

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

Related Questions