模板方法参数列表中“=0”

ech*_*Lee 4 c++ c++11

我看到如下代码:

using EnableIfIntegral = typename std::enable_if<
    std::is_integral<T>::value || std::is_enum<T>::value, int>::type;

template <typename T, EnableIfIntegral<T> = 0>
constexpr int Seconds(T n) {
  return time_internal::FromInt64(n, std::ratio<1>{});
}

template <std::intmax_t N>
constexpr int FromInt64(int64_t v, std::ratio<1, N>) {
  // Do Something
}

Run Code Online (Sandbox Code Playgroud)

我明白什么是模板函数。为什么模板参数列表中,有SomeClass<T> = 0?我知道T是模板参数。

为什么是std::ratio<1, N>参数?

Sam*_*hik 6

我将回答你的两个问题中的第一个问题。

using EnableIfIntegral = typename std::enable_if<
    std::is_integral<T>::value || std::is_enum<T>::value, int>::type;
Run Code Online (Sandbox Code Playgroud)

我相当确定您不小心遗漏了前一行,这应该是

template<typename T>
Run Code Online (Sandbox Code Playgroud)

所以完整的声明是

template<typename T>
using EnableIfIntegral = typename std::enable_if<
    std::is_integral<T>::value || std::is_enum<T>::value, int>::type;
Run Code Online (Sandbox Code Playgroud)

这是一个难以吞咽的大药丸,我稍后会回来讨论这个声明的更详细的细节,但这里发生的是,如果星星和月亮恰好对齐,就会变成:

template<typename T>
using EnableIfIntegral = int;
Run Code Online (Sandbox Code Playgroud)

换句话说,模板类型只是花园品种的简单别名int。仅此而已。转到下一个声明:

template <typename T, EnableIfIntegral<T> = 0>
constexpr int Seconds(T n) {
Run Code Online (Sandbox Code Playgroud)

这简单地变成了

template <typename T, int = 0>
constexpr int Seconds(T n) {
Run Code Online (Sandbox Code Playgroud)

换句话说,一个简单的模板参数,一个int默认为 0 的常量。

就是这样,仅此而已。这绝对没有任何作用,而这正是这里的预期结果。

第一个模板参数T是从实际参数推导出来的Seconds()模板函数。然后第二个模板参数将T模板类型传递给EnableIfIntegral模板别名声明,希望如果所有星星和月亮都正确对齐,则不会执行任何操作。

那么,星星和月亮什么时候才能正确对齐呢?当推导的模板类型是某种整数类型或某种enum类型时。然后一切就正常了。

但如果你做了一些愚蠢的事情,比如:

std::string what_is_this;

Seconds(what_is_this);
Run Code Online (Sandbox Code Playgroud)

对将发生的情况的非常宽松的描述:第一个模板参数被Seconds推导为std::string。到目前为止一切都很好,但我们已经注定了,因为Seconds模板函数内部的内容将无法处理std::string值。通常,这通常会导致典型的、难以理解的(对于普通人来说)C++ 编译器错误消息。

但事情不会发展到这一步。首先发生的情况是,推导的T模板参数(现在为 )std::string被转发到EnableIfIntegral,并且此模板别名将无法解析。要了解所有血淋淋的细节,请查找std::enable_if您最喜欢的 C++ 书籍中的内容。具体如何发生的细节并不重要,重点是模板替换模板Seconds()本身将会失败。编译器将找不到Seconds与此函数调用相匹配的合适模板。

这种情况通常会从编译器中产生更简单的错误消息。您的编译器的错误消息将非常基本,类似于“嘿,这里调用的 Seconds 模板是什么?我对此一无所知”。您将查看代码并意识到“愚蠢的我,我正在传递 astd::string但我需要传递一个整数值”。哎呀。

总的来说,这只是一种常见的方法,可以在出现问题且无法编译时减轻使用 C++ 库的痛苦。如果没有这个技巧,编译器将会抱怨time_internal::FromInt64函数调用(基于问题中显示的代码),但该函数调用没有任何问题。问题确实在于无论调用什么Seconds,这就是问题所在,这种常见方法可以帮助编译器生成更好的错误消息。