为何std::assignable_from返回false?Apple Clang下C++代码疑问
我正在使用Apple Clang编译C++代码,代码如下:
struct Foo { int x; Foo& operator=(const Foo&) = default; }; struct Bar { double x; Bar& operator=(const Foo& foo) { this->x = static_cast<double>(foo.x); return *this; } }; int main(int argc, const char * argv[]) { static_assert(std::assignable_from<Bar&, const Foo&>, "fail"); return 0; }
我已在Bar中定义了从Foo赋值的运算符,但std::assignable_from<Bar&, const Foo&>仍返回false,导致静态断言失败。我认为该概念应返回true,因为Bar对象可通过自定义赋值运算符从Foo对象赋值。请问这是编译器问题,还是我对std::assignable_from的理解有误?能否解释返回false的原因及修正方法?
原因分析
这不是编译器问题,而是对std::assignable_from概念的理解偏差。C++20中std::assignable_from<LHS, RHS>的要求远不止“存在可行的赋值操作”,它还包含两个关键额外条件:
LHS必须是左值引用类型(你的代码中Bar&满足这一点);- 必须满足
std::common_reference_with<const std::remove_reference_t<LHS>&, const std::remove_reference_t<RHS>&>,也就是const Bar&和const Foo&需要存在公共引用类型。
你的代码中,Foo和Bar是完全独立的结构体,两者之间没有继承关系或隐式转换路径,因此不存在公共引用类型,导致std::assignable_from的条件不满足,最终返回false。
另外需要确认:代码必须包含<concepts>头文件,且编译时启用C++20(如添加编译参数-std=c++20),否则std::assignable_from本身无法被正确识别。
修正方法
根据需求,有两种常见解决思路:
思路1:改用std::is_assignable_v替代
如果你只是想检查“Bar&能否被const Foo&赋值”,不需要满足std::assignable_from的严格约束,可以使用std::is_assignable_v,它仅验证赋值表达式的有效性,不要求公共引用类型:
#include <type_traits> // 替换静态断言为 static_assert(std::is_assignable_v<Bar&, const Foo&>, "fail");
思路2:满足std::assignable_from的全部条件
如果你确实需要std::assignable_from返回true,需要让const Bar&和const Foo&存在公共引用类型,常见实现方式是添加隐式转换:
比如给Foo添加转换为Bar的运算符:
struct Foo { int x; Foo& operator=(const Foo&) = default; // 添加隐式转换 operator Bar() const { return Bar{static_cast<double>(x)}; } };
此时const Foo&可以隐式转换为const Bar&,两者的公共引用类型为const Bar&,std::assignable_from的条件就会全部满足。
注意:这种方式会改变Foo和Bar的类型转换规则,需要根据实际业务场景评估是否合适。
内容的提问来源于stack exchange,提问作者Jinwoo Kim

