std::assignable_from (since C++20)

From cppreference.com
< cpp‎ | concepts
Defined in header <concepts>
template< class LHS, class RHS >

concept assignable_from =
    std::is_lvalue_reference_v<LHS> &&
    std::common_reference_with<
        const std::remove_reference_t<LHS>&,
        const std::remove_reference_t<RHS>&> &&
    requires(LHS lhs, RHS&& rhs) {
        { lhs = std::forward<RHS>(rhs) } -> std::same_as<LHS>;

    };
(since C++20)

The concept assignable_from<LHS, RHS> specifies that an expression of the type and value category specified by RHS can be assigned to an lvalue expression whose type is specified by LHS.

Semantic requirements

Given

  • lhs, an lvalue that refers to an object lcopy such that decltype((lhs)) is LHS,
  • rhs, an expression such that decltype((rhs)) is RHS,
  • rcopy, a distinct object that is equal to rhs,

assignable_from<LHS, RHS> is modeled only if

  • std::addressof(lhs = rhs) == std::addressof(lcopy) (i.e., the assignment expression yields an lvalue referring to the left operand);
  • After evaluating lhs = rhs:
    • lhs is equal to rcopy, unless rhs is a non-const xvalue that refers to lcopy (i.e., the assignment is a self-move-assignment),
    • if rhs is a glvalue:
      • If it is a non-const xvalue, the object to which it refers is in a valid but unspecified state;
      • Otherwise, the object it refers to is not modified;

Equality preservation

Expressions declared in requires expressions of the standard library concepts are required to be equality-preserving (except where stated otherwise).

Notes

Assignment need not be a total function. In particular, if assigning to some object x can cause some other object y to be modified, then x = y is likely not in the domain of =. This typically happens if the right operand is owned directly or indirectly by the left operand (e.g., with smart pointers to nodes in a node-based data structure, or with something like std::vector<std::any>).

Example

#include <atomic>
#include <concepts>
#include <string>
 
int main()
{
    // Normal basic usage, checks lvalue reference assignment
    static_assert(std::is_assignable_v<int&, int>);
    static_assert(std::assignable_from<int&, int>);
 
    static_assert(std::is_assignable_v<std::string&, std::string>);
    static_assert(std::assignable_from<std::string&, std::string>);
 
    // Fundamental types don't support assignment to an rvalue
    static_assert(!std::is_assignable_v<int, int>);
    static_assert(!std::assignable_from<int, int>);
 
    // std::assignable_from doesn't accept all valid assignment expressions:
 
    // rvalue reference assignment
    static_assert(std::is_assignable_v<std::string&&, std::string>);
    static_assert(!std::assignable_from<std::string&&, std::string>);
 
    // rvalue assignment
    static_assert(std::is_assignable_v<std::string, std::string>);
    static_assert(!std::assignable_from<std::string, std::string>);
 
    // std::atomic::operator= returns by value
    static_assert(std::is_assignable_v<std::atomic<int>&, int>);
    static_assert(!std::assignable_from<std::atomic<int>&, int>);
}

References

  • C++23 standard (ISO/IEC 14882:2024):
  • 18.4.8 Concept assignable_from [concept.assignable]
  • C++20 standard (ISO/IEC 14882:2020):
  • 18.4.8 Concept assignable_from [concept.assignable]

See also

checks if a type has an assignment operator for a specific argument
(class template)