C++20中定义依赖其他concept的concept为何失效?求排查
问题分析与修复
你的代码存在两处关键错误,导致publisher concept的验证逻辑未按预期生效:
1. event concept的定义方式错误
原代码中event concept的requires块写法无效:
template <typename t> concept event = requires { std::default_initializable<t>; std::copy_constructible<t>; std::move_constructible<t>; };
requires块要求的是可合法编译的表达式,而std::default_initializable<t>是一个编译期布尔值,并非表达式。这种写法只会检查该布尔值是否能被求值(总是成立),并没有真正验证t是否满足对应的concept。
正确写法是直接组合标准concept:
template <typename t> concept event = std::default_initializable<t> && std::copy_constructible<t> && std::move_constructible<t>;
2. publisher concept的验证逻辑未生效
原代码在requires表达式中调用lambda的方式,仅会验证lambda调用是否合法,不会检查lambda返回的布尔值是否为true。requires表达式的核心是判断“表达式是否可编译”,而非表达式的结果。因此即使lambda返回false,只要调用合法,publisher concept就会认为条件满足。
正确的做法是将tuple元素的验证作为concept的直接约束,通过类型展开结合折叠表达式实现:
方法一:直接内联验证逻辑
template <typename T> concept publisher = requires { typename T::events_published; } && []<typename... Events>(std::type_identity<std::tuple<Events...>>) { return (event<Events> && ...); }(std::type_identity<typename T::events_published>{});
方法二:拆分辅助concept复用
template <typename Tuple> concept tuple_of_events = []<typename... Events>(std::type_identity<std::tuple<Events...>>) { return (event<Events> && ...); }(std::type_identity<Tuple>{}); template <typename T> concept publisher = requires { typename T::events_published; } && tuple_of_events<typename T::events_published>;
修改后完整代码
#include <concepts> #include <tuple> #include <type_traits> template <typename t> concept event = std::default_initializable<t> && std::copy_constructible<t> && std::move_constructible<t>; struct ev_1 { ev_1() = default; ev_1(const ev_1 &) = default; ev_1(ev_1 &&) = default; }; struct ev_2 { ev_2() = delete; ev_2(const ev_2 &) = default; ev_2(ev_2 &&) = default; }; template <typename Tuple> concept tuple_of_events = []<typename... Events>(std::type_identity<std::tuple<Events...>>) { return (event<Events> && ...); }(std::type_identity<Tuple>{}); template <typename T> concept publisher = requires { typename T::events_published; } && tuple_of_events<typename T::events_published>; struct pub_a { using events_published = std::tuple<ev_1>; }; struct pub_b { using events_published = std::tuple<ev_2>; }; template <publisher t_publisher> void f() {} int main() { f<pub_a>(); // 正常通过编译 f<pub_b>(); // 触发编译错误,符合预期 return 0; }
修改后用g++ 11.4.0编译时,f<pub_b>()会因为ev_2不满足std::default_initializable,进而不满足event concept,最终导致pub_b不符合publisher约束,触发编译错误,与你的预期一致。
内容的提问来源于stack exchange,提问作者canellas
相关产品推荐
相关产品推荐

