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

如何以最简方式编写自由函数获取C++类的公有成员?

实现与直接访问成员语义完全一致的getBar函数

这个问题源于对另一Stack Overflow问题及其解答的思考。

假设有一个无法修改的类Foo,它拥有一个public成员bar,但没有对应的getter方法。我们需要编写一个getBar函数,使得getBar(expr-of-type-Foo)与expr-of-type-Foo.bar的语义完全一致——无论传入的expr-of-type-Foo是Foo类型的prvalue、xvalue还是lvalue。

给定的Foo定义如下:

struct Foo {
    int bar{};
    ~Foo() { bar = -1; } // 在多数编译器中,这有助于"验证"对象已销毁
};

初始实现

我最初的实现方案是通过重载处理不同值类别的输入,借助boost::hana::overload来组合重载逻辑:

constexpr auto getBar = overload(
    [](auto& foo) -> decltype(auto) { return (foo.bar); },
    [](auto const& foo) -> decltype(auto) { return (foo.bar); },
    [](auto&& foo) -> auto { return foo.bar; }
);

注:-> auto是冗余的,但在此场景下更具说明性。

简化思路

我尝试对初始实现进行简化,逐步优化:

  • 第一步:明确引用返回类型
    既然auto&/auto const&重载分别匹配非const左值和const左值,我们可以把返回类型从decltype(auto)改为明确的引用类型,同时去掉return表达式的括号:

    constexpr auto getBar = overload(
        [](auto& foo) -> auto& { return foo.bar; },
        [](auto const& foo) -> auto const& { return foo.bar; },
        [](auto&& foo) -> auto { return foo.bar; }
    );
    
  • 第二步:合并左值重载
    进一步思考后发现,auto&重载无法匹配const左值,此时const左值会自然匹配到对应重载,因此可以简化为只保留两个重载:auto&处理非const左值,auto&&处理右值和const左值,此时返回值的语义依然和直接访问bar一致:

    constexpr auto getBar = overload(
        [](auto& foo) -> auto& { return foo.bar; },
        [](auto&& foo) -> auto { return foo.bar; }
    );
    

目前我想不到进一步简化的方法。

测试代码

以下是验证上述实现的测试代码:

#include<assert.h>
#include<boost/hana/functional/overload.hpp>

struct Foo {
    int bar{};
    ~Foo() {
        bar = -1;
    }
};

int main() {
    {
        constexpr auto getBar = overload(
            [](auto&& foo) -> auto { return foo.bar; },
            [](auto& foo) -> auto& { return foo.bar; }
        );
        {
            Foo foo{3};
            assert(&getBar(foo) == &foo.bar);
            assert(getBar(foo) == 3);

            foo.bar = 4;
            assert(foo.bar == 4);
            assert(getBar(foo) == 4);

            getBar(foo) = 5;
            assert(foo.bar == 5);
            assert(getBar(foo) == 5);
        }
        {
            Foo const foo{3};
            assert(&getBar(foo) == &foo.bar);
            assert(getBar(foo) == 3);

            //foo.bar = 3;     // 预期编译失败
            //getBar(foo) = 3; // 预期编译失败
        }
        {
            auto const& foobar = getBar(Foo{3});
            assert(foobar == 3);
            //foobar = 5; // 预期编译失败
        }
        {
            //auto& foobar = getBar(Foo{3}); // 预期编译失败
            //auto& foobar = Foo{3}.bar;     // 预期编译失败
        }
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 06:05:19