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

std::begin(X)与X.begin()处理右值的差异:是标准疏漏还是使用错误?

std::begin与容器成员begin()处理右值的行为差异

普遍认知里std::begin(c)和c.begin()是等价的,但实际处理右值容器时,两者的行为存在反直觉的差异:

  • 调用容器的begin()&&(右值成员版本)会返回普通迭代器(iterator)
  • 调用std::begin处理右值容器时,会匹配到const C&版本的重载,返回常量迭代器(const_iterator)

这并非编译器(GCC/Clang)的疏漏,而是C标准的规定:std::begin并未提供针对右值引用的重载,右值容器会被绑定到const C&参数上,因此触发常量版本的迭代器返回;而容器自身的begin()&&是C17起新增的成员函数,专门用于右值对象,允许调用者修改右值容器内的元素(毕竟右值的生命周期局限于当前表达式,修改操作是合法的)。

验证代码

#include <unordered_map>
#include <type_traits>

using MyMap = std::unordered_map<int, int>;
using Iter = typename MyMap::iterator;
using ConstIter = typename MyMap::const_iterator;

// 右值容器调用成员begin(),返回普通迭代器,断言通过
static_assert(std::is_same_v<decltype(MyMap().begin()), Iter>);
// std::begin处理右值,匹配const&重载返回const_iterator,断言失败
static_assert(std::is_same_v<decltype(std::begin(MyMap())), Iter>); 

// 右值容器的成员begin()不返回const_iterator,断言失败
static_assert(std::is_same_v<decltype(MyMap().begin()), ConstIter>); 
// std::begin处理右值返回const_iterator,断言通过
static_assert(std::is_same_v<decltype(std::begin(MyMap())), ConstIter>);

临时解决方案(Workaround)

通过自定义模板优先调用容器自身的begin()成员,完美转发参数以匹配右值版本:

template<class T> concept has_begin = requires (T t) { t.begin(); };

template<class T>
decltype(auto) my_begin(T&& x) {
  if constexpr(has_begin<std::remove_reference_t<T>>)
    return std::forward<T>(x).begin();
  else return std::begin(x);
}

受影响的示例代码

以下代码在传入非const右值时会失效:

template<class T>
decltype(auto) do_things(T&& in) {
    const auto _end = in.end();
    auto it = std::begin(std::forward<T>(in));
    while(it != _end) {
        it->second += it->first; // 无法修改:std::begin返回const_iterator,指向的元素是const的
        ++it;
    }
    return std::forward<T>(in);
}

注:部分自定义类的begin()&&与begin()&行为差异明显,必须通过右值传递才能触发预期逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 11:43:29