为什么;“通用参考资料”;是否具有与右值引用相同的语法? 我刚刚研究了那些(相当)新的特性,我想知道为什么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; }
但有一件可疑的事:智能感知加下划线f(a)。我做了一些研究,我知道这是因为类型崩溃(Scott Meyers命名的通用引用),所以我想知道g++是怎么想的。当然,它没有被编译。微软以更直观的方式实现了他们的编译器,这非常好,但我不确定这是否符合标准,也不确定IDE是否应该有这种区别(编译器与intellisense,但事实上可能有某种意义)。好,回到问题上来。我是这样解决的:为什么;“通用参考资料”;是否具有与右值引用相同的语法? 我刚刚研究了那些(相当)新的特性,我想知道为什么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; },c++,c++11,reference,universal,rvalue,C++,C++11,Reference,Universal,Rvalue,但有一件可疑的事:智能感知加下划线f(a)。我做了一些研究,我知道这是因为类型崩溃(Scott Meyers命名的通用引用),所以我想知道g++是怎么想的。当然,它没有被编译。微软以更直观的方式实现了他们的编译器,这非常好,但我不确定这是否符合标准,也不确定IDE是否应该有这种区别(编译器与intellisense,但事实上可能有某种意义)。好,回到问题上来。我是这样解决的: template <typename T> void f(T& a) { std::cout
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";
}
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&&>());
}
模板
空f(T&a)
{
std::cout您回答了自己的问题:“通用引用”只是引用折叠的右值引用案例的名称。如果引用折叠需要其他语法,则不再是引用折叠。引用折叠只是将引用限定符应用于引用类型
所以我想知道g++是怎么想的,当然它没有编译
您的第一个示例是格式良好的。GCC4.9编译了它,没有任何抱怨,并且输出与MSVC一致
几乎是这样,因为我想,如果我想改变对象状态中通过右值引用传递的某些内容,该怎么办
右值引用不适用于const
语义;您始终可以更改通过move
传递的对象的状态。它们的用途需要可变性。尽管存在const&
之类的东西,但您不应该需要它。通用引用可以工作,因为c++11中的引用折叠规则。如果你有
template <typename T> func (T & t)
模板函数(T&T)
引用折叠仍会发生,但不会与临时引用一起使用,因此引用不是“通用的”。通用引用称为“通用的”,因为它可以接受lvals和rvals(还保留其他限定符)。t&t
不是通用的,因为它不能接受rvals
总而言之,通用引用是引用崩溃的产物,通用引用之所以这样命名是因为它是通用的,它可以是什么,我认为它是以另一种方式发生的。最初的想法是在语言中引入右值引用,意思是“提供双符号和引用的代码不关心引用的对象会发生什么”。这允许移动语义。这很好
现在,标准禁止对引用构造引用,但这始终是可能的。请考虑:
template<typename T>
void my_func(T, T&) { /* ... */ }
// ...
my_func<int&>(a, b);
现在,我们有两种引用,&
表示“我不关心对象可能发生的事情”,而&
表示“我可能关心对象可能发生的事情,所以你最好注意你在做什么。”“考虑到这一点,崩溃规则自然地流动:C++只应在没有人关心对象发生什么情况下将引用分解为<代码>和<代码>:< /p>
& & -> &
& && -> &
&& & -> &
&& && -> &&
有了这些规则,我想是Scott Meyers注意到了这些规则的子集:
& && -> &
&& && -> &&
表明&&
对于引用折叠是正确的中性,并且,当发生类型推断时,T&&
构造可用于匹配任何类型的引用,并创造了术语“通用引用”这不是委员会发明的东西,它只是其他规则的副作用,不是委员会的设计
因此,引入该术语是为了区分真正的右值引用,当没有发生类型推断时,它们保证是&
,以及那些类型推断的通用引用,它们不保证在模板专门化时保持&
。其他人已经提到了引用c套用规则是通用引用工作的关键,但还有另一个(可以说)同样重要的方面:当模板参数的形式为T&&
时,模板参数推断
事实上,关于这个问题:
// Example 1, sans f(T&):
#include <iostream>
#include <type_traits>
template <typename T>
void f(T&&) {
std::cout << "f(T&&) for universal references\n";
std::cout << "T&& is an rvalue reference: "
<< std::boolalpha
<< std::is_rvalue_reference<T&&>::value
<< '\n';
}
int main() {
int a;
const int b = 42;
f(a);
f(b);
f(0);
}
为什么“通用引用”与右值引用具有相同的语法
在我看来,模板参数的形式更为重要,因为这与语法有关。在C++03中,模板函数无法知道所传递对象的值类别(右值或左值)。C++11更改了模板参数推断,以说明这一点:14.8.2.1[temp.decrete.call]/p3
[…]如果p
是对cv非限定模板参数的右值引用,且参数是左值,则类型推断使用“对a
的左值引用”代替a
这比最初提议的措辞(由以下人员给出)要复杂一些:
如果p
是形式为cvT&
的右值引用类型,其中T
是模板类型参数,并且参数是左值,T
的推导模板参数值是a&
[示例:
template<typename T> int f(T&&);
int i;
int j = f(i); // calls f<int&>(i)
模板int f(T&&);
int i;
int j=f(i);//调用f(i)
---[结束示例]
更详细地说,上面的调用触发了f(int&&&&)
的实例化,在应用引用折叠后,该实例化将成为f(int&)
。另一方面f(0)
实例化f(int&&&)
(注意,内部没有&
)
没有其他形式的声明会将T
推断为int&
并触发实例化
template<typename T> int f(T&&);
int i;
int j = f(i); // calls f<int&>(i)
// Example 1, sans f(T&):
#include <iostream>
#include <type_traits>
template <typename T>
void f(T&&) {
std::cout << "f(T&&) for universal references\n";
std::cout << "T&& is an rvalue reference: "
<< std::boolalpha
<< std::is_rvalue_reference<T&&>::value
<< '\n';
}
int main() {
int a;
const int b = 42;
f(a);
f(b);
f(0);
}