切换基于模板类型 C++ 的引用参数传递

Posted

技术标签:

【中文标题】切换基于模板类型 C++ 的引用参数传递【英文标题】:Toggle pass by reference parameter based on template type C++ 【发布时间】:2020-08-16 19:55:54 【问题描述】:

我正在实现一个“starts_with”函数来检查一个字符串是否以某个前缀开头。我希望该函数能够互换地比较std::stringstd::string_view。我遇到的问题是当 std::string 作为参数传递时,我希望它通过引用传递,std::string_view 通过值传递。

目前我有这个设置:

#include <string_view>
#include <utility>

template <typename String>
struct string_type 
  using type = const String&;
;

template <>
struct string_type<std::string_view> 
  using type = const std::string_view;
;

template <typename String>
using string_type_t = typename string_type<String>::type;

template <typename String, typename Prefix>
bool string_starts_with(string_type_t<String> str, string_type_t<Prefix> pre) 
  if (pre.length() > str.length()) 
    return false;
  

  for (auto ch = std::pairstr.begin(), pre.begin();
       ch.second != pre.end();
       ++ch.first, ++ch.second) 
    if (*ch.first != *ch.second) 
      return false;
    
  

  return true;


int main() 
  using namespace std::string_view_literals;

  return string_starts_with("hello"sv, "hel"sv) ? 0 : 1;

但是 gcc 和 clang(已测试 here)无法推断出模板参数,我必须明确指定类型 string_starts_with&lt;std::string_view, std::string_view&gt;(..., ...)

一个明显的解决方案是为std::string_view 提供重载,但我需要用基本相同的主体(string_starts_with(std::string, std::string)string_starts_with(std::string, std::string_view)string_starts_with(std::string_view, std::string_view)string_starts_with(std::string_view, std::string))实现 4 个不同的函数。这可能仍然是可管理的,但如果我想将另一个类似字符串的对象(例如 std::vector&lt;char&gt;std::array&lt;char&gt; 引入 API)它就会变得无法管理。

【问题讨论】:

为什么不一直使用std::string_view 参数呢?它可以从 std::string 隐式构造 正如@IgorTandetnik 所说,只需string_view 即可。这也是 STL 对 starts_with 所做的事情。 完全忘记了!我想对于其他类似字符串的对象,我可以在函数调用中构造一个string_view template &lt;typename String, typename Prefix&gt; bool string_starts_with(const String&amp; str, const Prefix&amp; pre) ... 也不能为string_views 工作吗?为什么需要按值传递? @TedLyngmo 我相信,由于string_view 只是一个指向开始的指针和一个大小,它可以打包到 2 个寄存器中(至少在某些 ABI 上),因此不会发生间接寻址,编译器可以更好地优化代码。 【参考方案1】:

嵌套类型别名会禁用演绎。因为编译器无法猜测嵌套参数可能定义在哪个类中。您可以定义特征模板和/或使用元编程结构(enable_ifif constexprconcept/requiresstatic_assert.. .) 约束模板:

template<typename > 
struct str_ref_traits: std::false_type;
template<>
struct str_ref_traits<std::string&>: std::true_type;
template<>
struct str_ref_traits<std::string_view>: std::true_type;

template <typename S, typename P>
bool string_starts_with(S str, P pre) 
    static_assert(str_ref_traits<S>::value);
    static_assert(str_ref_traits<P>::value);
    if (pre.length() > str.length())
        return false;
    return std::equal(begin(pre), end(pre), begin(str));
;

当然,正如 cmets 中所述,std::string_view 可以处理 std::string - 设计使然。但是我想提一下嵌套类型别名不能推导出的一般规则。 其实std::type_identity的设计目标就是故意用作推理禁用器。

问候, 调频。

【讨论】:

以上是关于切换基于模板类型 C++ 的引用参数传递的主要内容,如果未能解决你的问题,请参考以下文章

C++学习35 模板中的函数式参数

深入了解C++:auto与函数模板之推导规则辨析

关于java中函数参数传递的两种方式的总结

C++ Primer 5th笔记(chap 16 模板和泛型编程)模板实参推断和引用

C语言中的参数传递方式都有哪些

C++中函数参数的传递方式有哪几种