2012-08-04 1 views
9

§21.4.5 [string.access]Является реализатором реализации std :: string, где 's.c_str() + s.size()' не обязательно совпадает с '& s [s.size()]'?

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) за каждые i в [0,size()].

Это сделало бы вышеупомянутое 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; 
} 

Теперь, очевидный вопрос ...

ли выше код действительно совместимый или я что-то упускать из вида?

+0

Одна вещь, которую я замечаю, заключается в том, что если вы на самом деле писали выписку для строкового объекта 'str':' char * p = str.c_str(); size_t i = str.size(); assert (p + i == & str [i]); 'утверждение не сработает с вашим кодом. Стандарт, похоже, не указывает конкретный контекст, в котором должен храниться инвариант, поэтому я должен быть осторожным, полагая, что он должен только удерживаться перед возвратом 'c_str()'. – pmdj

ответ

4

Игнорирование данного кода, рассматривая только вопрос, я думаю, что

  • , к сожалению, ответ, кажется, “ да ” и
  • , что, конечно, не намерения стандарта ,

Следовательно, это дефект .

Проверка list of known library defects, очевидно, эта проблема еще не сообщается.

Итак, как я уже говорил в чате, я рекомендую размещать его на [comp.std.C++] для того, чтобы решить вопрос о том, что на самом деле является ли дефект, и если это так, чтобы получить его в список дефектов и фиксированный.

+4

У меня смутное подозрение, что это намеренно. Эта формулировка позволяет отображать пустые строки с помощью одного «статического символа» где-то (без этой формулировки нулевой байт должен быть уникальным для каждой строки, для чего требуется (1) пустые строки, представляемые с помощью встроенного объекта нуль-байта или (2), требующий динамически распределенного буфера). Я не уверен, почему это было бы предпочтительнее (1), но кажется странным совпадением, что эта формулировка делает возможным, поэтому я подозреваю, что это намеренно – jalf

+0

@jalf: SSO - это маленькие строки без динамического распределения памяти. Это было бы идеально для этого нулевого байта, поэтому я не вижу, как это будет проблемой. Не могли бы вы объяснить? –

+1

@Matthieu: Но SSO не является обязательным? – ildjarn

0

Я не вижу, как это может быть совместимо. Пользовательский код никогда не сможет увидеть обещанное возвращенное значение.Код assert в коде вводит в заблуждение, потому что он не в том месте: функция еще не вернулась. Возврат: Требования применяются к значениям, возвращаемым функцией, а не к некоторому значению в его реализации (должно быть очевидно, почему это бессмысленная идея).

Утверждение должно быть здесь:

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

Я считаю, что формулировка, которая относится к s[s.size()] специально просто имел в виду, чтобы запретить вам взорвать нулевой терминатор.

+0

Чтобы не взорвать нулевой ограничитель, они могли просто сказать «не изменять значение, на которое ссылается' s [s.size()] '», и не нужно было бы допускать это конкретное значение вне последовательности. – Xeo