关于std::assignable_from的两个技术问题:std::common_reference_with作用及用法正确性
std::assignable_from in C++20 The Role of std::common_reference_with in std::assignable_from
Great question—this constraint is easy to overlook, but it’s critical for enforcing meaningful assignment semantics, not just syntactic correctness. Let’s break it down:
What the constraint actually checks
The std::common_reference_with<const std::remove_reference_t<LHS>&, const std::remove_reference_t<RHS>&> requirement ensures there’s a shared reference type that can bind to both a const lvalue of the underlying LHS type and a const lvalue of the underlying RHS type. In plain terms: it verifies the two types are logically compatible enough that assigning one to the other makes intuitive sense.
Practical examples
Valid case:
int&andlongstd::remove_reference_t<int&>isint,std::remove_reference_t<long>islongconst int&andconst long&shareconst long&as a common reference type (sinceintimplicitly converts tolong)- The assignment
int& = longis valid and returnsint&, sostd::assignable_from<int&, long>holds true.
Invalid case:
std::string&andintconst std::string&andconst int&have no common reference type—there’s no single reference that can bind to both a const string and a const int.- This means
std::assignable_from<std::string&, int>is false, which blocks nonsensical code likestd::string s; s = 42;from even passing the concept check (before the compiler hits the actual assignment error).
Inheritance case:
Derived&andBase- If
Derivedpublicly inherits fromBase,const Derived&andconst Base&shareconst Base&as a common reference type. - The assignment
Derived& = Base(which performs slicing) is valid, so the concept is satisfied.
- If
Why this matters
Without this constraint, std::assignable_from would only check if the assignment operator exists syntactically. But concepts are designed to express semantic intent—we want "assignable from" to mean the assignment is logically meaningful, not just that the compiler accepts it. This constraint weeds out edge cases where a type might have an assignment operator for an unrelated type (e.g., a custom X with operator=(Y) where X and Y have no logical overlap) but shouldn’t be considered "assignable from" in the general sense.
Is the Example Usage of std::assignable_from Correct?
Short answer: No—the constraint is misused here, and the function will never be instantiated correctly.
Let’s look at the code again:
template <typename D, std::integral T> requires std::assignable_from<D, T> void assign_the_thing(D& dest, T&& x) { dest = std::forward<T>(x); }
The problem
The first template parameter of std::assignable_from must be an lvalue reference type—check the concept definition:
template< class LHS, class RHS > concept assignable_from = std::is_lvalue_reference_v<LHS> && // ... other constraints ...
In your example, D is a non-reference type (e.g., int, std::string), so std::is_lvalue_reference_v<D> is false. This means the requires clause will never be satisfied, and the function will never be selected by overload resolution.
The fix
You need to pass an lvalue reference type as the first argument to std::assignable_from. The correct constraint is std::assignable_from<D&, T>:
template <typename D, std::integral T> requires std::assignable_from<D&, T> void assign_the_thing(D& dest, T&& x) { dest = std::forward<T>(x); }
A more concise alternative (C++20)
You can use abbreviated template syntax with concepts to make this cleaner:
void assign_the_thing(std::assignable_from auto& dest, std::integral auto&& x) { dest = std::forward<decltype(x)>(x); }
This version automatically ensures dest is an lvalue reference that’s assignable from x’s type, and x is an integral type.
内容的提问来源于stack exchange,提问作者Baykov Nikita




