Xeo
Xeo

Reputation: 131799

Is a std::string implementation conformant where 's.c_str() + s.size()' is not necessarily the same as '&s[s.size()]'?

§21.4.5 [string.access]

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

Returns: *(begin() + pos) if pos < size(). Otherwise, returns a reference to an object of type charT with value charT(), where modifying the object leads to undefined behavior.

The second part implies, to me atleast, that this "object of type charT" may reside outside of the sequence stored in the std::string object. Example implementation of a conforming operator[]:

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

Now, c_str()/data(), are specified in terms of operator[]:

§21.4.7 [string.accessors]

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

Returns: A pointer p such that p + i == &operator[](i) for each i in [0,size()].

This would make the above operator[] implementation non-conformant, as p + size() != &operator[](size()). However, with a bit of trickery, you can circumvent this problem:

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;
}

Now, the obvious question is...

Is the above code really conformant or did I overlook something?

Upvotes: 10

Views: 364

Answers (2)

R. Martinho Fernandes
R. Martinho Fernandes

Reputation: 234504

I don't see how it could be conformant. User code can never observe the promised returned value. The assert in the code is misleading because it is in the wrong place: the function has not returned yet. Returns: requirements apply to be value returned from the function, not to some value within its implementation (it should be obvious why that is a nonsensical idea).

The assertion should be here:

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

I believe the wording that treats s[s.size()] specially is simply meant to forbid you from blowing up the null terminator.

Upvotes: 0

Cheers and hth. - Alf
Cheers and hth. - Alf

Reputation: 145279

Ignoring the given code, considering only the question, I think that

  • unfortunately, the answer seems to be “yes”, and
  • that is certainly not the intent of the standard.

Hence, it appears to be a defect.

Checking the list of known library defects apparently this issue has not yet been reported.

So, as I stated in chat, I recommend posting it to [comp.std.c++], in order to get resolved the question of whether it really is a defect, and if so, to get it into the defects list and fixed.

Upvotes: 4

Related Questions