2012-08-04 16 views
9

§21.4.5 [string.access]Là một std :: chuỗi thực hiện conformant nơi 's.c_str() + s.size()' không nhất thiết phải giống như '& s [s.size()]'?

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

Returns:*(begin() + pos) nếu pos < size(). Nếu không, trả về một tham chiếu đến một đối tượng thuộc loại charT với giá trị charT(), nơi sửa đổi đối tượng dẫn đến hành vi không xác định.

Phần thứ hai ngụ ý, với tôi ít nhất, rằng "đối tượng kiểu charT" này có thể nằm ngoài chuỗi được lưu trữ trong đối tượng std::string. thực hiện ví dụ về một phù hợp operator[]:

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

Bây giờ, c_str()/data(), được quy định tại các điều khoản của operator[]:

§21.4.7 [string.accessors]

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

Returns: Một con trỏ p như vậy p + i == &operator[](i) cho mỗi i trong [0,size()].

Điều này sẽ làm cho việc triển khai trên operator[] không phù hợp, như p + size() != &operator[](size()). Tuy nhiên, với một chút thủ đoạn gian trá, bạn có thể phá vỡ vấn đề này:

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

Bây giờ, câu hỏi rõ ràng là ...

Là mã trên thực sự tuân thủ QTI hoặc tôi đã bỏ qua một cái gì đó?

+0

Một điều tôi nhận thấy là nếu bạn thực sự viết kiểm tra cho một đối tượng chuỗi 'str':' char * p = str.c_str(); size_t i = str.size(); khẳng định (p + i == & str [i]); 'xác nhận sẽ thất bại với mã của bạn. Tiêu chuẩn dường như không chỉ định một bối cảnh cụ thể mà bất biến phải giữ, vì vậy tôi sẽ cẩn thận về việc giả sử nó chỉ cần giữ trước khi 'c_str()' trả về. – pmdj

Trả lời

4

Bỏ qua mã nhất định, xem xét chỉ có câu hỏi, tôi nghĩ rằng

  • không may, câu trả lời có vẻ là “ có ”, và
  • đó chắc chắn không phải là ý định của tiêu chuẩn .

Do đó, nó có vẻ là một lỗi .

Kiểm tra list of known library defects dường như vấn đề này chưa được báo cáo.

Vì vậy, như tôi đã nói trong cuộc trò chuyện, tôi khuyên bạn nên gửi nó cho [comp.std.C++], để có được giải quyết câu hỏi liệu nó thực sự là một khiếm khuyết, và nếu như vậy, để có được nó vào danh sách lỗi và cố định.

+4

Tôi có một nghi ngờ mơ hồ rằng đây là cố ý. Từ ngữ này cho phép các chuỗi rỗng được biểu diễn bằng một 'char tĩnh' ở đâu đó (không có từ ngữ này, byte rỗng sẽ phải là duy nhất cho mỗi chuỗi, sẽ yêu cầu (1) chuỗi rỗng được biểu diễn bằng cách sử dụng một đối tượng trong null-byte, hoặc (2) yêu cầu một bộ đệm được cấp phát động). Tôi không chắc tại sao điều này sẽ thích hợp hơn (1), nhưng có vẻ như một sự trùng hợp kỳ lạ là từ ngữ này làm cho nó có thể, vì vậy tôi nghi ngờ nó có chủ ý – jalf

+0

@jalf: SSO là tất cả về chuỗi nhỏ mà không phân bổ bộ nhớ động. Nó sẽ là hoàn hảo cho byte null này vì vậy tôi không thấy làm thế nào nó sẽ là một vấn đề. Bạn có phiền không? –

+1

@Matthieu: Nhưng SSO không bắt buộc phải không? – ildjarn

0

Tôi không thấy nó có thể phù hợp như thế nào. Mã người dùng không bao giờ có thể quan sát giá trị trả về đã hứa.Mã số assert bị sai lệch vì mã nằm sai vị trí: hàm chưa trả về. Trả về: yêu cầu áp dụng cho giá trị được trả về từ hàm, chứ không phải cho một số giá trị trong quá trình thực hiện của nó (nên rõ ràng lý do tại sao đó là một ý tưởng vô nghĩa).

Khẳng định nên ở đây:

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

Tôi tin rằng từ ngữ mà đối xử với s[s.size()] đặc biệt chỉ đơn giản có nghĩa là để cấm bạn từ thổi lên terminator null.

+0

Để ngăn không cho tôi từ bỏ trình kết thúc null, họ có thể vừa nói "đừng sửa đổi giá trị được tham chiếu bởi' s [s.size()] '" và sẽ không cần phải cho phép giá trị cụ thể này tồn tại bên ngoài trình tự. – Xeo

Các vấn đề liên quan