c++ 从全称引用推导时是否丢弃const?

jei2mxaa  于 2023-07-01  发布在  其他
关注(0)|答案(2)|浏览(106)

我在学习通用(转发)引用时发现了这个问题。代码如下。

#include <iostream> // std::cout

#include <type_traits>

template <class T> // deductions among universal reference
void fun(T &&b)    // fold reference, maintain const
{
    std::cout << b << " ";
    std::cout << std::is_const<T>::value << " ";
    std::cout << std::is_lvalue_reference<T>::value << " ";
    std::cout << std::is_rvalue_reference<T>::value << std::endl;
}

int main()
{
    int a = 1;
    fun(a);            // lvalue: T=int&
    fun(std::move(a)); // rvalue: T=int

    const int ca = 1;
    fun(ca);            // const_lvalue: T=int& (why no const?)
    fun(std::move(ca)); // const_rvalue: T=const int

    int &la = a;
    fun(la); // lvalue_ref: T=int&

    int &&ra = 1;
    fun(ra);                             // rvalue_ref: T=int& (r_ref is an l_val)
    fun(std::forward<decltype(ra)>(ra)); // rvalue_ref + perfect forwarding T=int

    const int &cla = a;
    fun(cla); // const_lvalue_ref: T=int& (no const?)

    const int &&cra = 1;
    fun(cra); // const_rvalue_ref: T=int& (no const?)

    return 0;
}

其结果如下。可以看出,当输入参数是lvalue时,在解析T的类型时,const丢弃。然而,当它是r_value时,const保持
此外,当我尝试使用cppinsights运行这段代码时,它会生成4模板专门化

/* First instantiated from: insights.cpp:18 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
void fun<int &>(int & b)
{

}
#endif

/* First instantiated from: insights.cpp:19 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
void fun<int>(int && b)
{

}
#endif

/* First instantiated from: insights.cpp:22 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
void fun<const int &>(const int & b)
{

}
#endif

/* First instantiated from: insights.cpp:23 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
void fun<const int>(const int && b)
{

}
#endif

从结果中可以看出,const在cppinsights中的解析过程后被维护
有人能告诉我为什么我的代码和cppinsights的结果不一样吗?(为什么我的代码中的l_value被推导为T&而不是const T&?)
换句话说,问题的正确答案是什么:从universal reference推导const是否会被丢弃?

1tu0hz3e

1tu0hz3e1#

T是从带有左值参数的转发引用推导出来的,那么T是左值引用类型,并且对于任何引用类型

std::is_const<T>::value

总是false,因为引用类型不能是const限定的。你真正感兴趣的是引用的类型是否是“lvalue/rvalue-reference to X”,其中X是某种const限定类型,即是否

std::is_const<std::remove_reference_t<T>>::value

true当我们通俗地说引用是const限定的,我们实际上是指这个,而不是引用类型本身实际上是const限定的。

ut6juiuv

ut6juiuv2#

const永远不会被隐式丢弃,你使用std::is_const测试某个东西是否“是const”的代码只是有缺陷。std::is_const不考虑引用类型的常量:

std::is_const_v<const int> // true
std::is_const_v<const int&> // false, a const& is not a const type,
                            // it refers to something const

考虑一下你的代码中发生了什么:

const int ca = 1;
fun(ca); // calls fun<const int&>(ca);

// then:
template <class T> // T = const int&
void fun(T &&b)    // b is declared as const int&
{
    // ...
    std::cout << std::is_const<T>::value << " "; // prints 0, same reason as above
    // ...

当绑定到const L类型的左值时,转发引用T&&T推导为const L&。引用折叠后,T&&就变成了const L&
要使代码得到预期的结果,必须用途:

std::is_const<typename std::remove_reference<T>::type>::value
// or since C++14
std::is_const<std::remove_reference_t<T>>::value
// or since C++17
std::is_const_v<std::remove_reference_t<T>>

相关问题