You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

关于std::assignable_from的两个技术问题:std::common_reference_with作用及用法正确性

Understanding 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& and long

    • std::remove_reference_t<int&> is int, std::remove_reference_t<long> is long
    • const int& and const long& share const long& as a common reference type (since int implicitly converts to long)
    • The assignment int& = long is valid and returns int&, so std::assignable_from<int&, long> holds true.
  • Invalid case: std::string& and int

    • const std::string& and const 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 like std::string s; s = 42; from even passing the concept check (before the compiler hits the actual assignment error).
  • Inheritance case: Derived& and Base

    • If Derived publicly inherits from Base, const Derived& and const Base& share const Base& as a common reference type.
    • The assignment Derived& = Base (which performs slicing) is valid, so the concept is satisfied.

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

火山引擎 最新活动