C++ 入门模板通用参考和参数推导
C++ primer template universal reference and argument deduction
你好,我有这个来自 C++ primer 的例子:
template <typename T>
void f(T&& x) // binds to nonconstant rvalues
{
std::cout << "f(T&&)\n";
}
template <typename T>
void f(T const& x) // lvalues and constant revalues
{
std::cout << "f(T const&)\n";
}
这是我测试输出的尝试:
int main(){
int i = 5;
int const ci = 10;
f(i); // f(T& &&) -> f(int&)
f(ci); // f(T const&) -> f(int const&)
f(5); // f(T &&) -> f(int&&)
f(std::move(ci)); // f(T&&) -> f(int const&&)
cout << '\n';
}
输出:
f(T&&)
f(T const&)
f(T&&)
f(T&&)
在书中,据说采用转发引用的 f
版本仅绑定到非常量右值,但在 main
中我传递了一个常量右值 f(5)
和 f(std::move(ci))
但仍然是调用的第一个版本。
有没有办法调用第二个版本 f(T const&)
传递一个右值?我知道我可以明确地做到这一点:f<int const&>(5);
或 f<int const&>( std::move(ci) );
但我想知道在哪里可以调用传递右值?谢谢!
我认为 f
的转发参考版本后的注释不正确:f(T&&) // binds to nonconstant rvalues
因为它可以绑定到常量右值,例如:
f(std::move(ci)); // f(int const&&) and not
f(int const&)
书中的评论显然不完全正确。当您有
的两个可用重载时
template <typename T> void f(T&& x);
template <typename T> void f(T const& x);
它们都可以用任何参数调用(除了一些例外,我将在这里省略),但如果参数是 首选 第二个 =14=]左值。由于推导模板参数时适用的引用折叠规则,第一个在所有其他情况下都是首选。
但是,假设 T
由封闭的 class:
固定
template <class T>
struct S {
void f(T&& x);
void f(T const& x);
};
并假设 T
是非 const
对象类型。现在,情况不同了,因为调用 f
时没有推导 T
。书中的 评论 似乎指的是后一种情况,其中第一个 S::f
现在可以用 [=15] 类型的非 const
右值调用=] 但不是 const
类型 T
的右值,也不是(可能 const
)T
的左值。不幸的是,代码与评论不符。
让我们回到书中的实际代码。正如我所说,当参数是 const
左值时,采用 T const&
的函数将是首选。假设参数是 5
,但您想要显式调用第二个函数,即使它通常不会被选中。有几种方法可以做到这一点。最容易阅读的是将参数实际转换为 const
左值:
f((const int&)5);
您对 f<const int&>(5)
的建议也有效,但有点令人困惑。之所以令人困惑,是因为它需要代码的 reader 来实际执行精神上的引用折叠,然后记住第一个重载不如第二个重载专门化。第三种方法是:
static_cast<void(*)(int const&)>(&f)(5);
虽然这部分是最难读的,但只要需要提取一个特定的重载并将其绑定到函数指针,就可以使用 (5)
之前的部分。
你好,我有这个来自 C++ primer 的例子:
template <typename T> void f(T&& x) // binds to nonconstant rvalues { std::cout << "f(T&&)\n"; } template <typename T> void f(T const& x) // lvalues and constant revalues { std::cout << "f(T const&)\n"; }
这是我测试输出的尝试:
int main(){
int i = 5;
int const ci = 10;
f(i); // f(T& &&) -> f(int&)
f(ci); // f(T const&) -> f(int const&)
f(5); // f(T &&) -> f(int&&)
f(std::move(ci)); // f(T&&) -> f(int const&&)
cout << '\n';
}
输出:
f(T&&)
f(T const&)
f(T&&)
f(T&&)
在书中,据说采用转发引用的
f
版本仅绑定到非常量右值,但在main
中我传递了一个常量右值f(5)
和f(std::move(ci))
但仍然是调用的第一个版本。有没有办法调用第二个版本
f(T const&)
传递一个右值?我知道我可以明确地做到这一点:f<int const&>(5);
或f<int const&>( std::move(ci) );
但我想知道在哪里可以调用传递右值?谢谢!我认为
f
的转发参考版本后的注释不正确:f(T&&) // binds to nonconstant rvalues
因为它可以绑定到常量右值,例如:f(std::move(ci)); // f(int const&&) and not f(int const&)
书中的评论显然不完全正确。当您有
的两个可用重载时template <typename T> void f(T&& x);
template <typename T> void f(T const& x);
它们都可以用任何参数调用(除了一些例外,我将在这里省略),但如果参数是 首选 第二个 =14=]左值。由于推导模板参数时适用的引用折叠规则,第一个在所有其他情况下都是首选。
但是,假设 T
由封闭的 class:
template <class T>
struct S {
void f(T&& x);
void f(T const& x);
};
并假设 T
是非 const
对象类型。现在,情况不同了,因为调用 f
时没有推导 T
。书中的 评论 似乎指的是后一种情况,其中第一个 S::f
现在可以用 [=15] 类型的非 const
右值调用=] 但不是 const
类型 T
的右值,也不是(可能 const
)T
的左值。不幸的是,代码与评论不符。
让我们回到书中的实际代码。正如我所说,当参数是 const
左值时,采用 T const&
的函数将是首选。假设参数是 5
,但您想要显式调用第二个函数,即使它通常不会被选中。有几种方法可以做到这一点。最容易阅读的是将参数实际转换为 const
左值:
f((const int&)5);
您对 f<const int&>(5)
的建议也有效,但有点令人困惑。之所以令人困惑,是因为它需要代码的 reader 来实际执行精神上的引用折叠,然后记住第一个重载不如第二个重载专门化。第三种方法是:
static_cast<void(*)(int const&)>(&f)(5);
虽然这部分是最难读的,但只要需要提取一个特定的重载并将其绑定到函数指针,就可以使用 (5)
之前的部分。