如何用if-constexpr和concepts检测特定增强策略类型实例
在Andrei Alexandrescu的《Modern C++ Design》一书中,增强策略的核心逻辑是:实现策略的类可以为增强策略提供可选成员函数。使用基础策略时,只要不调用这些可选函数,代码就能正常编译;如果调用了不支持该操作的策略的可选函数,则会触发编译错误。
基础示例代码如下:
struct BasicPolicy { static void foo() {} }; struct EnrichedPolicy { static void foo() {} static void extra() {} }; template <typename Policy> struct Implementer : public Policy { static void do_foo() { Policy::foo(); } static void do_extra() { Policy::extra(); } }; Implementer<BasicPolicy> impl; impl.do_foo(); impl.do_extra(); // 预期编译错误:BasicPolicy无extra()函数 Implementer<EnrichedPolicy> enriched_impl; enriched_impl.do_foo(); enriched_impl.do_extra(); // 正常:EnrichedPolicy有extra()函数
具体需求与当前实现问题
我需要编写一个以策略宿主类Implementer为模板参数的函数,根据其使用的策略类型进行编译期条件分支。现有一个Implementer变体,会根据增强策略类型暴露两个可选函数:
struct EnrichedPolicy { static void foo() {} static void extra() {} }; struct AnotherEnrichedPolicy { static void foo() {} static void even_more() {} }; template <typename Policy> struct Implementer : public Policy { static void do_foo() { Policy::foo(); } // 强制基础策略函数 static void do_extra() { Policy::extra(); } // 某增强策略的可选函数 static void do_much_more() { Policy::even_more(); } // 另一增强策略的可选函数 };
我尝试用requires约束编写use_impl函数,但无法正常工作:
template <typename Policy> void use_impl(Implementer<Policy> i) { if constexpr(requires { Implementer<Policy>::do_extra(); }) { std::cout << "Enriched policy\n"; } else if constexpr(requires { Implementer<Policy>::do_much_more(); }) { std::cout << "Different enriched policy\n"; } else { std::cout << "Basic policy\n"; } }
问题在于:do_extra()和do_much_more()属于Implementer类定义的一部分,语法上requires约束始终判定为有效,即使实际调用特定模板参数时会编译失败。测试结果不符合预期:
Implementer<BasicPolicy> impl; Implementer<EnrichedPolicy> enriched_impl; Implementer<AnotherEnrichedPolicy> another_enriched_impl; use_impl(impl); // 输出"Enriched policy"(不符合预期) use_impl(enriched_impl); // 输出"Enriched policy" use_impl(another_enriched_impl); // 输出"Enriched policy" impl.do_extra(); // 编译错误:非增强策略
现有可行方案的耦合问题
如果直接针对Policy模板参数编写约束,代码可以正常工作:
template <typename Policy> void use_policy(Implementer<Policy> i) { if constexpr(requires { Policy::extra(); }) { std::cout << "Enriched policy\n"; } else if constexpr(requires { Policy::even_more(); }) { std::cout << "Different enriched policy\n"; } else { std::cout << "Basic policy\n"; } } use_policy(impl); // 输出"Basic policy" use_policy(enriched_impl); // 输出"Enriched policy" use_policy(another_enriched_impl); // 输出"Different enriched policy"
但该方案存在耦合问题:直接检测底层策略的实现细节,而非Implementer对外暴露的接口。如果未来do_extra()或do_much_more()的实现逻辑发生改变(比如不再直接调用Policy::extra()),就需要同步修改策略约束,违背了封装原则。
提问
是否有更优方案,能让编译器识别特定模板参数Policy下,调用Implementer<Policy>::do_extra()或do_much_more()是否合法,从而让use_impl(impl)输出预期的"Basic policy"?同时避免直接耦合底层Policy的实现细节?
内容的提问来源于stack exchange,提问作者NotAProgrammer

