模板参数推导如何区分左值和 literal/compile-time 值
How does template argument deduction distinguish between an lvalue and a literal/compile-time value
这是OP对Is constexpr useful for overload的解决方案相关的问题。
基本上,他用
template<class T>
typename std::enable_if<std::is_arithmetic<T>::value, int>::type
f(T&& n) { ... }
和
template<class T>
typename std::enable_if<!std::is_arithmetic<T>::value, int>::type
f(T&& n) { ... }
知道是否调用了 f()
是编译时变量(例如文字:f(42)
)还是左值(例如局部变量:f(argc)
)作为其参数.
问:它是如何工作的? (我预计,在这两个调用中,都会调用第一个重载(即 std::is_arithmetic<T>::value == true
)
这是一个完整的例子:
#include <iostream>
#include <type_traits>
using std::cout;
using std::endl;
template<class T>
constexpr
typename std::enable_if<std::is_arithmetic<T>::value,
int>::type
inline f(T&& n)
{
//cout << "compile time" << endl;
return 1;
}
template<class T>
typename std::enable_if<!std::is_arithmetic<T>::value,
int>::type
inline f(T&& n)
{
//cout << "run time" << endl;
return 0;
}
int main(int argc, char* argv[])
{
const int rt = f(argc);
constexpr int ct = f(42);
cout << "rt: " << rt << endl;
cout << "ct: " << ct << endl;
}
表单的模板函数
template <typename T>
void func(T&& t);
看起来好像它采用了 r 值参考。但实际上 T&&
这就是 Scott Meyers 所说的 通用参考 ,也称为转发参考。根据参数的值类别,可能会发生不同的事情。让我们来看看每个案例:
t 是非常量左值,例如
int i = 0;
func(i);
在这种情况下,T被推导为int
的左值引用,即T=int&
.
t是一个const左值,例如
const int i = 1;
func(i);
类似地,在这种情况下T
被推导为const int&
。
t是一个右值,例如
func(1);
在这种情况下,T
被推断为 int
正如我们可能预期的那样
这些推论以这种方式发生的确切原因与引用折叠的规则有关;如果您有兴趣,我强烈建议您阅读 Scott Meyers 关于该主题的文章。
上面的最后一个案例也说明了一点,在C和C++中,文字(字符串文字除外)总是右值。
这与enable_if
有什么关系?好吧,如果你的 f
是用整数文字调用的,那么 T
会被推断为普通的 int
。显然,is_arithmetic<int>
为真,因此第二个函数得到 SFINAE 并调用第一个函数。
然而,当使用左值调用时,T
被推断为 (const) int&
。引用是不是算术,所以第一个函数消失,只留下第二个被调用。
这是OP对Is constexpr useful for overload的解决方案相关的问题。
基本上,他用
template<class T>
typename std::enable_if<std::is_arithmetic<T>::value, int>::type
f(T&& n) { ... }
和
template<class T>
typename std::enable_if<!std::is_arithmetic<T>::value, int>::type
f(T&& n) { ... }
知道是否调用了 f()
是编译时变量(例如文字:f(42)
)还是左值(例如局部变量:f(argc)
)作为其参数.
问:它是如何工作的? (我预计,在这两个调用中,都会调用第一个重载(即 std::is_arithmetic<T>::value == true
)
这是一个完整的例子:
#include <iostream>
#include <type_traits>
using std::cout;
using std::endl;
template<class T>
constexpr
typename std::enable_if<std::is_arithmetic<T>::value,
int>::type
inline f(T&& n)
{
//cout << "compile time" << endl;
return 1;
}
template<class T>
typename std::enable_if<!std::is_arithmetic<T>::value,
int>::type
inline f(T&& n)
{
//cout << "run time" << endl;
return 0;
}
int main(int argc, char* argv[])
{
const int rt = f(argc);
constexpr int ct = f(42);
cout << "rt: " << rt << endl;
cout << "ct: " << ct << endl;
}
表单的模板函数
template <typename T>
void func(T&& t);
看起来好像它采用了 r 值参考。但实际上 T&&
这就是 Scott Meyers 所说的 通用参考 ,也称为转发参考。根据参数的值类别,可能会发生不同的事情。让我们来看看每个案例:
t 是非常量左值,例如
int i = 0; func(i);
在这种情况下,T被推导为
int
的左值引用,即T=int&
.t是一个const左值,例如
const int i = 1; func(i);
类似地,在这种情况下
T
被推导为const int&
。t是一个右值,例如
func(1);
在这种情况下,
T
被推断为int
正如我们可能预期的那样
这些推论以这种方式发生的确切原因与引用折叠的规则有关;如果您有兴趣,我强烈建议您阅读 Scott Meyers 关于该主题的文章。
上面的最后一个案例也说明了一点,在C和C++中,文字(字符串文字除外)总是右值。
这与enable_if
有什么关系?好吧,如果你的 f
是用整数文字调用的,那么 T
会被推断为普通的 int
。显然,is_arithmetic<int>
为真,因此第二个函数得到 SFINAE 并调用第一个函数。
然而,当使用左值调用时,T
被推断为 (const) int&
。引用是不是算术,所以第一个函数消失,只留下第二个被调用。