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

如何通过模板在单个声明中重载const与非const运算符?

实现与目标类型行为一致的stalker模板类

要实现一个与Obj行为几乎一致的stalker<Obj>,核心是通过模板化运算符转发+完美转发避免手动重载所有运算符,同时利用decltype(auto)自动处理返回值(包括void),通过ref-qualifier区分const/非const、左值/右值版本。

1. 基础结构与构造函数

先定义持有目标对象的模板类,用完美转发构造参数初始化内部对象:

#include <utility>
#include <type_traits>

template <typename T>
class stalker {
private:
    T obj_;

public:
    // 完美转发构造函数,适配所有T的构造方式
    template <typename... Args>
    constexpr stalker(Args&&... args) noexcept(std::is_nothrow_constructible_v<T, Args...>)
        : obj_(std::forward<Args>(args)...) {}

2. 处理成员运算符(如[]、())

针对成员运算符,通过ref-qualifier重载区分const/非const、左值/右值场景,用decltype(auto)自动推导返回值,无需专门处理void返回的情况:

示例:下标运算符[]

// 辅助函数:复用下标逻辑
    template <typename Self, typename Index>
    static constexpr decltype(auto) subscript_impl(Self&& self, Index&& idx) noexcept(
        noexcept(std::forward<Self>(self).obj_[std::forward<Index>(idx)])
    ) {
        return std::forward<Self>(self).obj_[std::forward<Index>(idx)];
    }

    // 左值非const版本
    template <typename Index>
    constexpr decltype(auto) operator[](Index&& idx) & {
        return subscript_impl(*this, std::forward<Index>(idx));
    }

    // 左值const版本
    template <typename Index>
    constexpr decltype(auto) operator[](Index&& idx) const& {
        return subscript_impl(*this, std::forward<Index>(idx));
    }

    // 右值非const版本
    template <typename Index>
    constexpr decltype(auto) operator[](Index&& idx) && {
        return subscript_impl(std::move(*this), std::forward<Index>(idx));
    }

    // 右值const版本
    template <typename Index>
    constexpr decltype(auto) operator[](Index&& idx) const&& {
        return subscript_impl(std::move(*this), std::forward<Index>(idx));
    }

示例:函数调用运算符()

同理,处理函数调用运算符:

// 辅助函数:复用调用逻辑
    template <typename Self, typename... Args>
    static constexpr decltype(auto) call_impl(Self&& self, Args&&... args) noexcept(
        noexcept(std::forward<Self>(self).obj_(std::forward<Args>(args)...))
    ) {
        return std::forward<Self>(self).obj_(std::forward<Args>(args)...);
    }

    template <typename... Args>
    constexpr decltype(auto) operator()(Args&&... args) & {
        return call_impl(*this, std::forward<Args>(args)...);
    }

    template <typename... Args>
    constexpr decltype(auto) operator()(Args&&... args) const& {
        return call_impl(*this, std::forward<Args>(args)...);
    }

    template <typename... Args>
    constexpr decltype(auto) operator()(Args&&... args) && {
        return call_impl(std::move(*this), std::forward<Args>(args)...);
    }

    template <typename... Args>
    constexpr decltype(auto) operator()(Args&&... args) const&& {
        return call_impl(std::move(*this), std::forward<Args>(args)...);
    }

3. 处理一元运算符(如++、--、!)

针对一元运算符,同样通过ref-qualifier区分场景,根据目标类型的返回值决定返回stalker包装或原返回值:

// 前缀++
    constexpr decltype(auto) operator++() & noexcept(noexcept(++obj_)) {
        if constexpr (std::is_lvalue_reference_v<decltype(++obj_)>) {
            ++obj_;
            return *this; // 保持链式调用,返回stalker引用
        } else if constexpr (std::is_same_v<decltype(++obj_), void>) {
            ++obj_; // 返回void
        } else {
            // 返回包装后的stalker对象
            return stalker<std::decay_t<decltype(++obj_)>>(++obj_);
        }
    }

    // const版本前缀++(如果Obj支持const前缀++)
    constexpr decltype(auto) operator++() const& noexcept(noexcept(++std::as_const(obj_))) {
        if constexpr (std::is_lvalue_reference_v<decltype(++std::as_const(obj_))>) {
            ++std::as_const(obj_);
            return *this;
        } else if constexpr (std::is_same_v<decltype(++std::as_const(obj_)), void>) {
            ++std::as_const(obj_);
        } else {
            return stalker<std::decay_t<decltype(++std::as_const(obj_))>>(++std::as_const(obj_));
        }
    }

4. 处理二元运算符(如+、-、==)

二元运算符需要处理stalker在左右两边的情况,用友元模板实现,并根据返回值决定是否包装为stalker:

// 处理stalker在左边的+运算
    template <typename U, typename V>
    friend constexpr decltype(auto) operator+(stalker<U> lhs, V&& rhs) noexcept(
        noexcept(lhs.obj_ + std::forward<V>(rhs))
    ) {
        using result_t = decltype(lhs.obj_ + std::forward<V>(rhs));
        if constexpr (std::is_same_v<result_t, void>) {
            lhs.obj_ + std::forward<V>(rhs);
        } else {
            return stalker<std::decay_t<result_t>>(lhs.obj_ + std::forward<V>(rhs));
        }
    }

    // 处理stalker在右边的+运算
    template <typename U, typename V>
    friend constexpr decltype(auto) operator+(U&& lhs, stalker<V> rhs) noexcept(
        noexcept(std::forward<U>(lhs) + rhs.obj_)
    ) {
        using result_t = decltype(std::forward<U>(lhs) + rhs.obj_);
        if constexpr (std::is_same_v<result_t, void>) {
            std::forward<U>(lhs) + rhs.obj_;
        } else {
            return stalker<std::decay_t<result_t>>(std::forward<U>(lhs) + rhs.obj_);
        }
    }
};

5. 更简化的替代方案:继承(如果允许)

如果Obj不是final类,且允许直接继承,**public继承Obj**是最简洁的方案,自动继承所有运算符和成员函数:

template <typename T>
class stalker : public T {
public:
    using T::T; // 继承所有构造函数
    // 如需拦截操作,可重写对应的成员函数
};

此方案局限性:无法拦截所有操作(用户可直接转换为Obj*),且如果Obj有私有构造函数则无法使用。

关键说明

  • const/非const处理:通过ref-qualifier(&/const&/&&/const&&)重载,确保const stalker<Obj>和stalker<const Obj>都能调用对应const版本的运算符。
  • void返回处理:decltype(auto)会自动推导返回void,无需额外的constexpr分支,完全匹配Obj的行为。
  • 代码复用:通过辅助静态模板函数,将运算符的核心逻辑集中实现,减少重复代码。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 19:54:52