问题描述
我想再次确认我对前向重载的工作方式有正确的理解
template< class T >
constexpr T&& forward( std::remove_reference_t<T>& t ) noexcept;
template< class T >
constexpr T&& forward( std::remove_reference_t<T>&& t ) noexcept;
如果我们用一些局部变量 n
调用 forward,假设它是 int
,重载将被指定为
constexpr int&& forward( int& t ) noexcept;
constexpr int&& forward( int&& t ) noexcept;
所以我们会选择第一个。如果我们仅使用 4
调用 forward,则将指定相同的重载,但将选择第二个版本。所以第一个重载总是捕获所有的左值,然后是所有的右值。正确吗?
解决方法
是的。几乎。您不能只调用 forward
。
如果您使用局部变量 foward
(例如 n
)调用 int
:
int n;
forward(n);
你得到一个编译错误。如果您调用 forward<int>
:
int n;
forward<int>(n);
你得到两个重载候选:
constexpr int&& forward<int>(int& t)noexcept;
constexpr int&& forward<int>(int&& t)noexcept;
然后正常重载决议运行,并选择第一个。
如果我们这样做
forward(4);
再次,编译错误,因为 T
无法推导出,但如果我们这样做
forward<int>(4);
在以下两个重载之间选择:
constexpr int&& forward<int>(int& t)noexcept;
constexpr int&& forward<int>(int&& t)noexcept;
其中第二个被选中。
当我们这样做时,事情会变得更有趣
int n;
forward<int&>(n);
我们得到
constexpr int& forward<int&>(int& t)noexcept;
constexpr int& forward<int&>(int&& t)noexcept;
返回值从 int&&
变为 int&
,但参数没有变化。
参数上的删除引用既阻止了模板参数的推导,又确保引用折叠永远不会将它们变成左值引用。
所以第一个总是左值引用,第二个总是右值引用。
但要明确的是,您不能只调用 forward
-- 您必须为某种类型 forward<T>
调用 T
,而该类型 T
会改变重载的内容看起来像。在所有(几乎?)合理使用 forward
中,这并不重要,但不合理使用可能会产生影响。
struct unreasonable {
operator int&&()&;
};
unreasonable r;
forward<int>(r);
这将选择第二个重载。