You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何返回成员的副本而非引用?C++值类别与decltype(auto)解析

问题分析:左值成员返回为何未修改原对象

根据cppreference的说明,左值对象的成员同样是左值:

以下表达式为左值表达式:
a.m(对象成员表达式),除非m是成员枚举器、非静态成员函数,或者a是右值且m是对象类型的非静态数据成员;

我们来看一个示例程序:自定义类Test是int的包装类,添加了各类构造、赋值的打印语句;另有模板函数get,接受T的万能引用,用decltype(auto)返回其成员。

#include <iostream>

struct Test 
{
    int member;
    explicit Test(int member): member(member) { std::cout << "Ctor" << std::endl; }
    Test(const Test& other): member(other.member) { std::cout << "Copy ctor" << std::endl; }
    Test(Test&& other) noexcept: member(other.member) { std::cout << "Move ctor" << std::endl; }
    Test& operator=(const Test& other) { member = other.member; std::cout << "Copy =" << std::endl; return *this; }
    Test& operator=(Test&& other) { member = other.member; std::cout << "Move =" << std::endl; return *this; }
    ~Test() { std::cout << "Dtor" << std::endl; }
};

template <typename T>
decltype(auto) get(T&& obj)
{   
    std::cout << "Is obj lvalue ref? " << std::is_lvalue_reference_v<decltype(std::forward<T>(obj))> << std::endl;
    return std::forward<T>(obj).member;
}

int main()
{
    Test test{1234};
    auto&& ret = get(test);
    ret = 5678;
    std::cout << "test.member is equal to " << test.member << std::endl;
    return 0;
}

我对decltype(auto)的推导规则理解如下:

  • 对于prvalue,推导为其类型
  • 对于lvalue,推导为引用
  • 对于xvalue,推导为右值引用

但程序输出如下:

Ctor
Is obj lvalue ref? 1
test.member is equal to 1234
Dtor

main函数中的赋值ret = 5678并未改变test对象的成员值,说明返回的是成员副本,这和我的理解不符,这是为什么?


解答

问题出在decltype(auto)的推导细节上:decltype对表达式的推导规则分两种情况——

  1. 若表达式是无括号的标识符(比如直接访问的成员变量member),decltype会直接取该标识符的声明类型;
  2. 若表达式是带括号的复合表达式,decltype才会根据表达式的值类别推导类型。

你的return std::forward<T>(obj).member;语句中,std::forward<T>(obj).member是无括号的成员访问表达式,属于标识符范畴,因此decltype(auto)会直接推导为member的声明类型int,而非左值对应的int&。这就导致函数返回的是原成员的副本,而非引用,所以对ret的赋值不会影响原对象的test.member。

只需要给返回的成员表达式加上括号,让decltype(auto)根据值类别推导:

return (std::forward<T>(obj).member);

修改后,(std::forward<T>(obj).member)是带括号的左值表达式,decltype(auto)会推导为int&,函数返回原成员的引用。此时程序输出会变为:

Ctor
Is obj lvalue ref? 1
test.member is equal to 5678
Dtor

总结:decltype(auto)对无括号的成员/变量标识符,会直接取其声明的非引用类型;只有用括号包裹表达式,才会遵循值类别推导引用类型,这是容易被忽略的细节。


内容的提问来源于stack exchange,提问作者Mati

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 18:15:31