我看到了如下代码:
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
}
我了解模板功能是什么。为什么在模板参数列表中它有SomeClass<T> = 0
?我知道T
是模板参数。
为什么std::ratio<1, N>
是一个参数?
答案 0 :(得分:2)
我将回答您的两个问题中的第一个。
using EnableIfIntegral = typename std::enable_if<
std::is_integral<T>::value || std::is_enum<T>::value, int>::type;
我相当确定您不小心遗漏了上一行, 应该是
template<typename T>
所以完整的声明是
template<typename T>
using EnableIfIntegral = typename std::enable_if<
std::is_integral<T>::value || std::is_enum<T>::value, int>::type;
这是可吞咽的大药丸,稍后我将再讨论此声明的详细信息,但是这里发生的是,如果星星和月亮恰好被倾斜,则变为:< / p>
template<typename T>
using EnableIfIntegral = int;
换句话说,模板类型只是花园品种int
的简单别名。仅此而已。移至下一个声明:
template <typename T, EnableIfIntegral<T> = 0>
constexpr int Seconds(T n) {
这简直变成了
template <typename T, int = 0>
constexpr int Seconds(T n) {
换句话说,就是一个简单的模板参数,一个int
常量,默认为0。
就是这样,仅此而已。这绝对没有任何作用,而这恰恰是这里的预期结果。
第一个模板参数T
从实际参数推导到Seconds()
模板函数。然后,第二个模板参数将T
模板类型传递给EnableIfIntegral
模板别名声明,希望,如果所有的星星和月亮都正确对齐,则不会执行任何操作。
现在,星月何时正确对齐?这是推导的模板类型是某些整数类型或某些enum
类型的时候。然后一切都正常。
但是,如果您做一些愚蠢的事情,例如:
std::string what_is_this;
Seconds(what_is_this);
对将要发生的事情的描述非常宽松:Seconds
的第一个模板参数推导为std::string
。到目前为止,还不错,但是我们已经注定要失败,因为Seconds
模板函数中的内容将无法处理std::string
值。通常,这通常会导致出现典型的,难以理解的(简直是凡人)C ++编译器错误消息。
但是事情不会走得那么远。首先发生的是推导的T
模板参数(现在为std::string
)被转发到EnableIfIntegral
,并且此模板别名将无法解析。有关所有奇怪的原因,请查阅std::enable_if
在您最喜欢的C ++书中的功能。发生这种情况的确切细节不是很重要,关键是Seconds()
模板本身的模板替换将失败。编译器将找不到适合此函数调用的Seconds
模板。
这种情况通常会从您的编译器产生一条简单得多的错误消息。您的编译器的错误消息将是非常基本的,类似于“嘿,此Seconds模板在这里叫什么?对此我一无所知”。您将看一下代码并意识到“抱歉,我正在传递std::string
,但我需要传递一个整数值”。糟糕!
总的来说,这只是一种常见的方法,可以在出现问题且无法编译时减轻使用C ++库的痛苦。没有这个技巧,编译器会抱怨time_internal::FromInt64
函数调用(基于问题中显示的代码),但是该函数调用没有问题。问题出在哪里,实际上是无论调用什么Seconds
,这种通用方法都可以帮助编译器生成更好的错误消息。