【发布时间】:2013-12-20 06:52:03
【问题描述】:
我刚刚对那些(相当)新特性进行了一些研究,我想知道为什么 C++ 委员会决定为它们引入相同的语法?似乎开发人员不必浪费一些时间来了解它是如何工作的,而一种解决方案可以让我们考虑进一步的问题。就我而言,它是从可以简化为的问题开始的:
#include <iostream>
template <typename T>
void f(T& a)
{
std::cout << "f(T& a) for lvalues\n";
}
template <typename T>
void f(T&& a)
{
std::cout << "f(T&& a) for rvalues\n";
}
int main()
{
int a;
f(a);
f(int());
return 0;
}
我首先在 VS2013 上编译它,它按预期工作,结果如下:
f(T& a) for lvalues
f(T&& a) for rvalues
但是有一件可疑的事情:智能感知在 f(a) 下划线。我做了一些研究,我明白这是因为类型崩溃(Scott Meyers 命名它的通用引用),所以我想知道 g++ 是怎么想的。当然它没有编译。微软实现他们的编译器以更直观的方式工作是非常好的,但我不确定它是否符合标准以及IDE是否应该存在这种差异(编译器与智能感知,但实际上可能有点意思)。好的,回到问题。我是这样解决的:
template <typename T>
void f(T& a)
{
std::cout << "f(T& a) for lvalues\n";
}
template <typename T>
void f(const T&& a)
{
std::cout << "f(T&& a) for rvalues\n";
}
现在没有任何类型崩溃,只是 (r/l) 值的正常重载。它在 g++ 上编译,智能感知停止抱怨,我几乎满意。几乎,因为我想如果我想改变由右值引用传递的对象状态中的某些东西怎么办?我可以在必要时描述一些情况,但是这个描述太长了,不能在这里展示。我是这样解决的:
template <typename T>
void f(T&& a, std::true_type)
{
std::cout << "f(T&& a) for rvalues\n";
}
template <typename T>
void f(T&& a, std::false_type)
{
std::cout << "f(T&& a) for lvalues\n";
}
template <typename T>
void f(T&& a)
{
f(std::forward<T>(a), std::is_rvalue_reference<T&&>());
}
现在它可以在所有经过测试的编译器上编译,它允许我在右值引用实现中更改对象状态,但它看起来不太好,这是因为通用引用和右值引用的语法相同。所以我的问题是:为什么 C++ 委员会没有为通用引用引入另一种语法?我认为这个特性应该用信号来表示,例如,T?、auto? 或类似的东西,但不是 T&& 和 auto&&,它们只是与右值引用发生冲突。使用这种方法,我的第一个实现将是完全正确的,不仅适用于 MS 编译器。谁能解释委员会的决定?
【问题讨论】:
-
因为右值引用和通用引用密切相关,
T &引用也会发生折叠,但你不能有一个需要 lval 的临时 (&&) -
通用引用并不是一个“真实”的东西,因为它是一个特定的语言术语。所以委员会给他们一个语法的前提根本没有意义,因为委员会对语言中的东西没有概念。通用引用是指围绕右值引用和模板推导的行为,并非正式地引用它。语法相同的原因是因为它们是相同的东西:右值引用。通用引用只是引用右值引用行为的一个子集。
-
@Piotrek 不应该在第一个函数中说“f(T& a) for rvalues\n”吗?
-
到目前为止的答案并没有真正回答这个问题(Cassio Neri 的除外)。 “通用参考”不仅仅是正常参考行为的一个子集。区别在于只有当参数是
T&&,那么T才可以推导出为引用类型。与参数T或T&的情况相比,这是一种新行为,例如,在这种情况下,T永远不会被推导出为引用类型。 (类型推导后发生引用折叠)。 OP 正在询问为什么相同的旧语法突然有一个新案例的理由,而不是引入新语法。 -
在 C++11 的开发过程中,有些人认为应该使用新的语法,因为令人困惑的是,
T可以根据参数列表中T的使用方式推断出引用类型.但他们失去了选票。
标签: c++ c++11 reference universal rvalue