c++ 为什么右值引用模板变量能够绑定到左值

k75qkfdt  于 2023-05-08  发布在  其他
关注(0)|答案(2)|浏览(135)

为什么右值引用模板变量b<int>能够绑定到左值a

#include <iostream>

int a = 3;

template<typename T>
T&& b = a;

int main() {
    if(std::is_same_v<decltype(b<int>), int&&>) {
        std::cout << "same\n";
    } else {
        std::cout << "different\n";
    }
}
Output: same
vs3odd8k

vs3odd8k1#

为什么右值引用模板变量b<int>能够绑定到左值a
这个问题做出了一个不正确的假设。右值引用b<int>无法绑定到a。使用您的实现,不会出现错误,因为decltype(b<int>)本身不会导致b<int>的示例化。
这是好还是不好,有点不清楚。C++17标准草案的相应引用如下[temp.inst/6]:
除非变量模板专门化已经被显式地示例化或显式地专门化,否则变量模板专门化在使用专门化时被隐式地示例化
根据这句话,我认为应该示例化b,因为使用了b<int>(即使在未评估的上下文中)。
然而,在目前的草案中,措辞有所不同[temp.inst/7/1]:
除非变量模板特化是声明的特化,否则当在需要变量定义存在的上下文中引用变量模板特化时,或者如果定义的存在影响程序的语义时,变量模板特化被隐式示例化。
无论如何,一旦你确定示例是必需的,你就会得到一个编译错误。
现场演示:https://godbolt.org/z/Ws5ajxWYM

htrmnn0y

htrmnn0y2#

根据 * 引用折叠规则 *,“右值引用到右值引用折叠到右值引用,所有其他组合形成左值引用”。虽然标准用其他的话来表述它(C++17标准草案[dcl.ref/6])。
因此,对于变量b

T = int    ==> T&& becomes                           int&&
T = int&   ==> T&& becomes int& &&  ==> collapses to int&
T = int&&  ==> T&& becomes int&& && ==> collapses to int&&

回到你的代码:

template<typename T>
T&& b = a;

// On different instantiations this generates:
int&& b<int> = a;    // error, can't bind rvalue reference to lvalue
int& b<int&> = a;    // ok
int&& b<int&&> = a;  // error, can't bind rvalue reference to lvalue

从这些文件中,只有b<int&>可以编译(演示Godbolt)。
那么,为什么即使b<int>不能编译,decltype(b<int>)也能通过编译呢?这是因为decltype操作符只检查声明是否格式良好,而不执行任何示例化,因此不验证定义是否格式良好。请参阅丹尼尔的答案,以获得基于C++标准措辞的更深入的解释。

相关问题