为什么;“通用参考资料”;是否具有与右值引用相同的语法? 我刚刚研究了那些(相当)新的特性,我想知道为什么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++委员会决定为它们都引入相同的语法?似乎开发人员必须浪费一些时间来理解它是如何工作的,有一种解决方案可以让他们考虑进一步的问题。在我的例子中,它是从问题开始的,可以简化为: #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

但有一件可疑的事:智能感知加下划线f(a)。我做了一些研究,我知道这是因为类型崩溃(Scott Meyers命名的通用引用),所以我想知道g++是怎么想的。当然,它没有被编译。微软以更直观的方式实现了他们的编译器,这非常好,但我不确定这是否符合标准,也不确定IDE是否应该有这种区别(编译器与intellisense,但事实上可能有某种意义)。好,回到问题上来。我是这样解决的:

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
是形式为cv
T&
的右值引用类型,其中
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);
}