0%

C++特化模板的内部匹配参数代码解析

本文解析ghc::filesystem的一段模板代码,涉及模板特化/偏特化的参数也需要模板参数的情形,以及std::enable_if的使用。


C++特化模板的内部匹配参数代码解析

代码背景

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
template <class T>
struct _is_basic_string : std::false_type {};

template <class CharT, class Traits, class Alloc>
struct _is_basic_string<std::basic_string<CharT, Traits, Alloc>> : std::true_type {};

template <typename strT, typename std::enable_if<path::_is_basic_string<strT>::value, bool>::type = true>
GHC_INLINE bool startsWith(const strT& what, const strT& with)
{
return with.length() <= what.length() && std::equal(with.begin(), with.end(), what.begin());
}

template <typename strT, typename std::enable_if<path::_is_basic_string<strT>::value, bool>::type = true>
GHC_INLINE bool endsWith(const strT& what, const strT& with)
{
return with.length() <= what.length() && what.compare(what.length() - with.length(), with.size(), with) == 0;
}

这段代码来自ghc::filesystem

该代码实现了:

  • _is_basic_string:判断类型是否为 std::basic_string(如 std::string / std::wstring
  • startsWith / endsWith:模板函数,仅在参数类型是字符串时可用

问题及解析

_is_basic_string<strT>::value 来自哪里?

  • _is_basic_string 是自定义模板类,继承自 std::true_type / std::false_type
  • std::true_type / std::false_type 定义了 static constexpr bool value
  • _is_basic_string<strT>::value 实际上就是访问该静态布尔值

_is_basic_string 是一个模板参数,还是三个?

  • _is_basic_string 是一个 模板类只有一个模板参数 T

  • 特化模板:

    1
    2
    template <class CharT, class Traits, class Alloc>
    struct _is_basic_string<std::basic_string<CharT, Traits, Alloc>> : std::true_type {};
    • 这里的三个参数是用于匹配 T 内部结构,不是外层模板参数;这里模板偏特化,属于模板特化/偏特化
    • 外层模板参数仍然只有一个:T

可以继续对三个模板参数特化吗?

  • ✅ 可以通过 部分特化CharT / Traits / Alloc 进行细分

  • 例:

    1
    2
    template <class Traits, class Alloc>
    struct _is_basic_string<std::basic_string<char, Traits, Alloc>> : std::true_type {};
  • ❌ 不能直接“嵌套特化”内部三个参数,只能通过外层模板写新的部分特化


4. typename std::enable_if<...>::type = true 怎么写的?

  • std::enable_if<condition, bool>::type:如果 condition 为 true,则 type = bool,否则不存在

  • = true默认值,允许模板参数自动推导

  • SFINAE 逻辑:

    • 条件为 true → 模板启用
    • 条件为 false → 替换失败,模板被忽略

5. 默认值是必须的吗?

  • ❌ 不必须

  • ✅ 推荐保留,使模板参数可自动推导

  • 不写默认值时,需要调用时显式指定第二模板参数

  • 例:

    1
    startsWith<std::string, true>(...); // 这样才行,但很丑


6. = false 可行吗?

  • ✅ 可以
  • 功能与 = true 一样,SFINAE 逻辑不受影响
  • 仅影响模板参数默认值(签名上为 false),可读性稍差

7. typename = typename std::enable_if<...>::type 是否可行?

1
2
3
4
5
6
7
template <bool _Test, class _Ty = void>
struct enable_if {};

template <
typename strT,
typename = typename std::enable_if<path::_is_basic_string<strT>::value>::type
>

  • ✅ 可以

  • 不需要默认值

  • 简化写法,更现代、更可读

  • 不指定 enable_if 的第二个模板参数(默认为 void)

  • 直接用一个无名模板参数 typename = …

  • 当条件不满足时,std::enable_if::type 不存在 → SFINAE 触发

  • 当条件满足时,std::enable_if::type 为 void,模板启用

  • C++14 可进一步简化为:

    1
    template <typename strT, typename = std::enable_if_t<path::_is_basic_string<strT>::value>>

总结表格

问题 解析
_is_basic_string<strT>::value 来源 来自 _is_basic_string 模板,继承 std::true_type / std::false_typevalue
_is_basic_string 模板参数数目 外层只有 1 个模板参数 T;特化内部有三个用于匹配字符串类型
是否可对三个模板参数再特化 ✅ 可通过部分特化针对内部参数组合
typename std::enable_if<...>::type = true 作用 SFINAE 控制函数模板是否启用;= true 提供默认值便于自动推导
默认值是否必须 ❌ 不必须,但推荐保留以便调用时不用显式写模板参数
默认值是否可以 = false ✅ 可行,功能不变,仅影响默认签名值
是否可以用 typename = typename std::enable_if<...>::type ✅ 完全可行,更简洁,不需要默认值
C++14 推荐写法 typename = std::enable_if_t<condition>

示例:现代写法

1
2
3
4
5
6
template <typename strT, typename = std::enable_if_t<_is_basic_string<strT>::value>>
bool startsWith(const strT& what, const strT& with)
{
return with.length() <= what.length() &&
std::equal(with.begin(), with.end(), what.begin());
}
×