C++中声明不修改std::span的函数的最佳实践
如何通过函数类型区分修改/只读的std::span模板函数?
我有一批操作std::span的模板函数,部分函数会修改span指向的对象,另一部分则不会。我希望通过函数类型明确区分这两类函数,防止在只读函数中意外修改span。请问实现这一需求的最佳方式是什么?
无效方案示例
方案一:直接声明const版本的span参数
此方案会导致模板参数推导失败,无法直接传入非const的std::span:
#include <span> #include <string> using namespace std; template<typename B> void want_span(std::span<B>) {}; template<typename B> void want_cspan(std::span<const B>) {}; int main() { std::string r; want_span(std::span(r)); // OK want_cspan(std::span(r)); // 错误:无法推导B的类型 }
方案二:自定义cspan类型
自定义类型需要显式转换,且无法兼容原生std::span:
#include <span> #include <string> #include <ranges> using namespace std; template<typename B> struct cspan : std::span<const B> { using std::span<const B>::span; }; template<std::ranges::contiguous_range R> cspan(R &&) -> cspan<std::remove_const_t<std::ranges::range_value_t<R>>>; template<typename B> void want_cspan2(cspan<B>) {}; int main() { std::string r; want_cspan2(cspan(r)); // OK want_cspan2(std::span(r)); // 错误:无法匹配参数类型 }
最佳实践方案
方法1:重载+转发(兼容C++17及以上)
通过重载实现非conststd::span到const版本的自动转发,既保留函数类型的明确区分,又无需显式转换:
#include <span> #include <string> // 修改型函数:接受非const span,允许修改元素 template<typename B> void want_span(std::span<B>) {}; // 只读函数主版本:明确接受const span,保证只读 template<typename B> void want_cspan(std::span<const B>) { // 此处仅进行只读操作 }; // 重载:接受非const span,自动转发到const版本 template<typename B> void want_cspan(std::span<B> s) { want_cspan(std::span<const B>(s)); } int main() { std::string r; want_span(std::span(r)); // OK want_cspan(std::span(r)); // OK,自动转发到const版本 want_cspan(r); // 也可直接传入容器,推导为std::span<const char> }
方法2:C++20 auto参数简化声明
利用C++20的auto模板参数,直接声明只读函数的参数为std::span<const auto>,自动支持非const span的隐式转换:
#include <span> #include <string> template<typename B> void want_span(std::span<B>) {}; // 只读函数:明确声明为const span,自动兼容非const span的隐式转换 void want_cspan(std::span<const auto> s) { // 此处仅进行只读操作 }; int main() { std::string r; want_span(std::span(r)); // OK want_cspan(std::span(r)); // OK,隐式转换为std::span<const char> want_cspan(r); // OK,直接推导为std::span<const char> }
核心思路
- 修改型函数使用
std::span<B>作为参数,明确允许修改元素; - 只读型函数使用
std::span<const B>作为参数,从类型层面保证不会意外修改元素; - 通过重载或C++20的
auto参数,实现非const span到const span的自动转换,避免调用时的显式类型转换,保持代码简洁。
内容的提问来源于stack exchange,提问作者user3188445
相关产品推荐
相关产品推荐

