CodeNebula
C++20stladvanced

std::assignable_from (C++20 Addition)

C++20 · The Big Four · Compiler flag: -std=c++20

Explanation

Concept that verifies a type can be assigned from another type via the assignment operator. Checks that the assignment expression is valid and returns the correct reference type.

Real-World Scenario

Constrain a generic `fill` algorithm to only accept output types that can actually be assigned from the given value type.

Quick Snippet

snippet.cpp
#include <concepts>

template<typename Container, typename Value>
    requires std::assignable_from<
        std::ranges::range_reference_t<Container>, const Value&>
void fill_container(Container& c, const Value& val) {
    for (auto& elem : c) elem = val;
}

Full Example

assignable_from.cpp
// CodeNebula - C++20 Example: std::assignable_from Concept
// Compile: g++ -std=c++20 -Wall -Wextra -o example assignable_from.cpp

// std::assignable_from<LHS, RHS> checks that an expression of type RHS
// can be assigned to an lvalue of type LHS. The LHS must be an lvalue
// reference type.

#include <concepts>
#include <iostream>
#include <string>
#include <vector>

// Generic assign function constrained by assignable_from
template <typename T, typename U>
    requires std::assignable_from<T&, U>
void assign_to(T& target, U&& source) {
    target = std::forward<U>(source);
    std::cout << "Assignment successful\n";
}

// Fill a container element if assignment is valid
template <typename Container, typename Value>
    requires std::assignable_from<typename Container::reference, Value>
void fill_first(Container& c, Value&& val) {
    if (!c.empty()) {
        c.front() = std::forward<Value>(val);
        std::cout << "Filled first element\n";
    }
}

// Compile-time checks
template <typename LHS, typename RHS>
void check_assignable(const char* lhs_name, const char* rhs_name) {
    std::cout << lhs_name << " = " << rhs_name << ": "
              << std::assignable_from<LHS, RHS> << "\n";
}

int main() {
    // Basic assignment
    int x = 0;
    assign_to(x, 42);
    std::cout << "x = " << x << "\n";

    // String assignment from const char*
    std::string s = "hello";
    assign_to(s, "world");
    std::cout << "s = " << s << "\n";

    // Fill first element of vector
    std::vector<int> v = {1, 2, 3};
    fill_first(v, 99);
    std::cout << "v[0] = " << v[0] << "\n";

    // Check various assignability relationships
    std::cout << "\nAssignability checks:\n";
    check_assignable<int&, int>("int&", "int");
    check_assignable<int&, double>("int&", "double");
    check_assignable<std::string&, const char*>("string&", "const char*");
    check_assignable<double&, int>("double&", "int");
    check_assignable<std::string&, int>("string&", "int");
    check_assignable<int&, std::string>("int&", "string");

    return 0;
}

// Expected output:
// Assignment successful
// x = 42
// Assignment successful
// s = world
// Filled first element
// v[0] = 99
//
// Assignability checks:
// int& = int: 1
// int& = double: 1
// string& = const char*: 1
// double& = int: 1
// string& = int: 0
// int& = string: 0
Run command
g++ -std=c++20 -Wall -Wextra -o example assignable_from.cpp