模板类型的方法调用:兼容指针类型与派生检查问题
问题与解决方案
首先是定义的Policy基类:
template<typename Type> struct Policy { virtual bool cause() = 0; virtual void implement(std::shared_ptr<Type> type) = 0; };
初始实现的integrate方法:
template<template<typename> typename TPolicy, typename TObj> void integrate(TPolicy<TObj>&& policy, std::shared_ptr<TObj> val) { if (policy.cause()) policy.implement(val); }
面临两个问题:
- 如何让
integrate()同时支持Policy<T>*、shared_ptr<Policy<T>>等指针类型作为参数?当前实现的.运算符不支持指针调用。 - 如何在编译期检查
TPolicy是否确实派生自Policy<T>?
问题1:支持多种指针/引用类型的参数处理
核心是统一成员函数的访问方式,以下两种方案可选:
方案1:自定义解引用辅助模板
编写通用的解引用函数,把不同类型的参数转换成对象引用:
// 处理普通引用 template<typename T> T& deref(T& obj) { return obj; } // 处理原始指针 template<typename T> T& deref(T* ptr) { return *ptr; } // 处理shared_ptr template<typename T> T& deref(const std::shared_ptr<T>& ptr) { return *ptr; } // 扩展支持unique_ptr template<typename T> T& deref(const std::unique_ptr<T>& ptr) { return *ptr; }
修改integrate函数,用辅助函数统一访问:
template<typename TPolicy, typename TObj> void integrate(TPolicy&& policy, std::shared_ptr<TObj> val) { auto& policy_ref = deref(std::forward<TPolicy>(policy)); if (policy_ref.cause()) { policy_ref.implement(val); } }
方案2:使用C++17的std::invoke
std::invoke会自动处理对象、指针、智能指针的调用逻辑,无需手动写解引用:
#include <functional> template<typename TPolicy, typename TObj> void integrate(TPolicy&& policy, std::shared_ptr<TObj> val) { if (std::invoke(&Policy<TObj>::cause, std::forward<TPolicy>(policy))) { std::invoke(&Policy<TObj>::implement, std::forward<TPolicy>(policy), val); } }
问题2:编译期检查派生关系
方案1:static_assert + std::is_base_of(C++11及以上)
通过类型特性提取真实的Policy类型,再做编译期断言:
#include <type_traits> template<typename TPolicy, typename TObj> void integrate(TPolicy&& policy, std::shared_ptr<TObj> val) { // 移除引用和const/volatile限定符 using RawPolicy = std::remove_cv_t<std::remove_reference_t<TPolicy>>; // 提取指针/智能指针指向的原始类型 using ActualPolicy = std::conditional_t< std::is_pointer_v<RawPolicy>, std::remove_pointer_t<RawPolicy>, std::conditional_t< std::is_same_v<RawPolicy, std::shared_ptr<typename RawPolicy::element_type>>, typename RawPolicy::element_type, RawPolicy > >; // 编译期断言,确保派生关系 static_assert(std::is_base_of_v<Policy<TObj>, ActualPolicy>, "传入的Policy类型必须派生自Policy<TObj>"); auto& policy_ref = deref(std::forward<TPolicy>(policy)); if (policy_ref.cause()) { policy_ref.implement(val); } }
方案2:C++20概念约束
用概念直接约束模板参数,错误信息更友好:
#include <concepts> #include <functional> // 定义概念:验证类型是否符合Policy<TObj>的接口 template<typename TPolicy, typename TObj> concept PolicyCompatible = requires(TPolicy policy, std::shared_ptr<TObj> val) { std::invoke(&Policy<TObj>::cause, policy); std::invoke(&Policy<TObj>::implement, policy, val); }; template<PolicyCompatible<TObj> TPolicy, typename TObj> void integrate(TPolicy&& policy, std::shared_ptr<TObj> val) { if (std::invoke(&Policy<TObj>::cause, std::forward<TPolicy>(policy))) { std::invoke(&Policy<TObj>::implement, std::forward<TPolicy>(policy), val); } }
这个概念不仅检查派生关系,还直接验证成员函数的可调用性,适用性更强。
内容的提问来源于stack exchange,提问作者Mark Tikhonov
相关产品推荐
相关产品推荐

