【问题标题】:Is a std::string implementation conformant where 's.c_str() + s.size()' is not necessarily the same as '&s[s.size()]'?std::string 实现是否符合 's.c_str() + s.size()' 不一定与 '&s[s.size()]' 相同?
【发布时间】:2012-08-02 08:03:58
【问题描述】:

§21.4.5 [string.access]

const_reference operator[](size_type pos) const;
reference operator[](size_type pos);

返回: *(begin() + pos) 如果pos < size()。否则,返回对类型为 charT 且值为 charT() 的对象的引用,其中修改对象会导致未定义的行为。

第二部分至少对我来说意味着这个“charT 类型的对象”可能位于存储在std::string 对象中的序列之外。符合 operator[] 的示例实现:

reference operator[](size_type pos){
  static contexpr charT default = charT();
  if(pos == size())
    return default;
  return buf[pos];
}

现在,c_str()/data() 被指定为 operator[]

§21.4.7 [string.accessors]

const charT* c_str() const noexcept;
const charT* data() const noexcept;

返回: 一个指针p 使得p + i == &operator[](i) 对应[0,size()] 中的每个i

这将使上述operator[] 实现不符合要求,如p + size() != &operator[](size())。但是,通过一些技巧,您可以绕过这个问题:

reference operator[](size_type pos){
  static contexpr charT default = charT();
  if(pos == size() && !evil_context) // assume 'volatile bool evil_context;'
    return default;
  return buf[pos];
}

struct evil_context_guard{
  volatile bool& ctx;
  evil_context_guard(volatile bool& b)
    : ctx(b) {}
  ~evil_context_guard(){ b = false; }
};

const charT* c_str() const noexcept{
  evil_context_guard g(evil_context = true);
  // now, during the call to 'c_str()', the requirement above holds
  // 'p + i == &operator[](i) for each i in [0,size()]'
  const charT* p = &buf[0];
  assert(p+size() == &operator[](size()));
  return p;
}

现在,显而易见的问题是……

上面的代码真的符合还是我忽略了什么?

【问题讨论】:

  • 我注意到的一件事是,如果您实际上是在为字符串对象编写签出代码str:char* p = str.c_str(); size_t i = str.size(); assert(p + i == &str[i]);,那么您的代码中的断言将会失败。该标准似乎没有指定不变量必须保持的特定上下文,所以我会小心假设它只需要在 c_str() 返回之前保持。

标签: c++ string c++11 language-lawyer


【解决方案1】:

忽略给定的代码,只考虑问题,我认为

  • 很遗憾,答案似乎是“是”,并且
  • 这当然不是标准的意图

因此,这似乎是一个缺陷

检查list of known library defects 显然这个问题尚未报告。

所以,正如我在聊天中所说,我建议将其发布到 [comp.std.c++],以便解决它是否真的是缺陷的问题,如果是,将其放入缺陷列表并进行修复。

【讨论】:

  • 我隐约怀疑这是故意的。此措辞允许在某处使用单个 static char 表示空字符串(如果没有此措辞,空字节必须对每个字符串都是唯一的,这将需要 (1) 使用对象内 null 表示空字符串-byte,或 (2) 需要动态分配的缓冲区)。我不确定为什么这比(1)更可取,但这种措辞使之成为可能似乎是一个奇怪的巧合,所以我怀疑这是故意的
  • @jalf:SSO 就是在没有动态内存分配的情况下拥有小字符串。这对于这个空字节来说是完美的,所以我不明白这会是一个问题。你介意解释一下吗?
  • @ildjarn:不,显然不是,但它确实表明空字符串没有理由不能分配类内空字符。
  • @Matthieu :它也没有反驳该标准可能允许不提供 SSO 并且将从静态哨兵值中受益的实现,所以我不确定你的意思是什么.
【解决方案2】:

我看不出它怎么可能符合要求。用户代码永远无法观察到承诺的返回值。代码中的assert 具有误导性,因为它位于错误的位置:函数尚未返回。 返回: 要求适用于从函数返回的值,而不是其实现中的某个值(应该很明显为什么这是一个荒谬的想法)。

断言应该在这里:

auto p = s.c_str();
assert(p + s.size() == &operator[](s.size()));

我相信特别对待s[s.size()] 的措辞只是为了禁止你炸毁空终止符。

【讨论】:

  • 为了阻止我炸毁空终止符,他们可以说“不要修改 s[s.size()] 引用的值”,并且不需要允许这个特定值存在序列之外。
猜你喜欢
  • 2023-02-08
  • 2014-08-19
  • 2010-12-31
  • 2021-08-28
  • 1970-01-01
  • 2011-01-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多