2013-02-11 20 views
10

Các mã sau biên dịch tốt với gcc 4.7.2 (mingw)std :: unordered_map :: đặt vào một chỗ vấn đề với tư nhân/xóa bản sao constructor

#include <unordered_map> 
#include <tuple> 

struct test 
{ 
     test() =default; 
    private: 
     test(test const&) =delete; 
}; 

int main() 
{ 
    std::unordered_map<char, test> map; 

    map.emplace(
     std::piecewise_construct, 
     std::forward_as_tuple('a'), 
     std::forward_as_tuple() 
    ); 
} 

Nếu tôi thay đổi các nhà xây dựng bản sao trong testtest(test const&) =delete;-test(test const&) =default; tuy nhiên , lỗi mẫu có vẻ như bị khiếu nại về const test& không thể chuyển đổi thành test (văn bản here). Không nên làm việc? Hoặc nếu không, không nên cả hai đều đưa ra một lỗi?

Trả lời

11

Nếu bạn nhìn vào nôn mửa mẫu lỗi một cách cẩn thận hơn, bạn sẽ thấy đoạn này cà rốt trong đó:

test.exe.cpp:8:3: error: 'constexpr test::test(const test&)' is private 

Đây là đầu mối để các vấn đề.

GCC 4.7.2 không kiểm tra quyền truy cập như là một phần của khấu trừ đối số mẫu (như được yêu cầu bởi C++ 03.) Đặc điểm is_convertible được triển khai bằng SFINAE, dựa trên việc khấu trừ đối số mẫu và nếu độ phân giải quá tải chọn một đối số private constructor deduction thành công, nhưng sau đó kiểm tra truy cập thất bại vì hàm tạo được chọn là private. Đây là vấn đề với GCC 4.7 vì nó không được thay đổi để tuân thủ quy tắc C++ 11 mới trong 14.8.2 [temp.deduct] có nội dung:

-8- Nếu thay thế dẫn đến loại hoặc biểu thức không hợp lệ, loại khấu trừ không thành công. Một loại hoặc biểu thức không hợp lệ là loại hoặc biểu thức không hợp lệ nếu được viết bằng các đối số được thay thế. [Lưu ý: Kiểm tra truy cập được thực hiện như một phần của quá trình thay thế. -end note]

Đây là một sự thay đổi rất lớn để các quy tắc khấu trừ trước, trước đây rằng đoạn nói

-8- Nếu một kết quả thay thế trong một kiểu không hợp lệ hoặc biểu hiện, loại trừ thất bại . Một loại hoặc biểu thức không hợp lệ là loại hoặc biểu thức không hợp lệ nếu được viết bằng các đối số được thay thế. Kiểm tra truy cập không được thực hiện như là một phần của quá trình thay thế. Do đó, khi khấu trừ thành công, lỗi truy cập vẫn có thể xảy ra khi hàm được khởi tạo.

Sự thay đổi đã được thực hiện khá muộn trong quá trình C++ 0x bởi DR 1170, và làm cho SFINAE hoàn toàn awesome trong C++ 11 :)

GCC 4.8 thực hiện các quy định mới, vì vậy is_convertible và những đặc điểm tương tự đưa ra câu trả lời đúng cho các nhà thầu không thể tiếp cận.

4

Câu trả lời đúng là Jonathan Wakeley. Tôi sẽ để điều này vì nó cung cấp thông tin hữu ích cho những người có vấn đề tương tự liên quan đến insert.


Phiên bản ngắn gọn là do sự cố trong triển khai Thư viện chuẩn được sử dụng bởi GCC 4.7.2, dẫn đến từ ngữ gây hiểu lầm được sử dụng trong tiêu chuẩn C++ 11. Có một đề xuất thay đổi cho các từ ngữ, cũng như một sửa chữa của việc thực hiện trong GCC 4.8.


Long phiên bản

This GCC bug entry báo cáo một vấn đề rất tương tự, nơi insert được sử dụng thay vì emplace. Các libstdC++ thực hiện insert theo tiêu chuẩn, trong đó nêu về insert chức năng (cụ thể là, template <class P> pair<iterator,bool> insert(P&& obj)):

(§23.5.4.4/5) Ghi chú: chữ ký này sẽ không tham gia vào giải quyết tình trạng quá tải nếu P là ngầm mui trần đến value_type.

libstdC++ dường như đã thực hiện yêu cầu này bằng cách sử dụng câu hỏi enable_if kiểm tra std::is_convertible<> cho các loại có liên quan.

Báo cáo lỗi được liên kết ở trên các trạng thái sau đó thực sự cần sử dụng std::is_constructible<> và phải thay đổi từ ngữ trong tiêu chuẩn. Nó liên kết đến một vấn đề LWG (nhóm công tác ngôn ngữ), trong đó đề xuất một sự thay đổi tiêu chuẩn cho điều này đã (LWG issue #2005, xem Portland 2012 nhập cảnh, phần liên quan của sự thay đổi đề xuất dưới đây):

  1. Thay đổi 23.5.4.4 [unord.map.modifers] xung quanh p. 1 như được chỉ ra:

    template <class P> 
    pair<iterator, bool> insert(P&& obj); 
    

[...] Ghi chú: chữ ký này sẽ không tham gia vào giải quyết tình trạng quá tải nếu P là ngầm mui trần để VALUE_TYPE std::is_constructible<value_type, P&&>::value là đúng.

Sự thay đổi đề xuất cũng khẳng định rằng tác động của các insert chức năng mô tả ở trên nên được tương đương với của emplace(std::forward<P>(obj)). Do đó, có thể nói rằng vấn đề được mô tả trong câu hỏi của bạn chính xác là cùng một vấn đề.

Và quả thực, những thay đổi đề xuất dường như được phản ánh trong gần đây GCC 4.8 snapshot: Khi bạn biên dịch mã của bạn với GCC 4.8, việc kiểm tra is_convertible không được thực hiện và không có thông báo lỗi xuất hiện.

+1

Đóng, nhưng không có xì gà :) LWG 2005 chỉ áp dụng cho 'chèn' không' emplace', lý do chương trình không thành công với GCC 4.7.2 là nó không kiểm tra quyền truy cập như một phần của trích dẫn đối số mẫu (như được yêu cầu trong C++ 03), do đó 'is_constructible' bị lỗi truy cập do hàm tạo riêng. GCC 4.8 thực hiện quy tắc C++ 11 và kiểm tra truy cập trong quá trình trích đối số mẫu –

+2

Bạn có thể xác nhận sự khác biệt không phải do bất kỳ thay đổi nào trong thư viện chuẩn bằng cách xử lý trước mã bằng G ++ 4.7 (vì vậy nó sử dụng thư viện từ 4.7) rồi compilng với 4.8, trong trường hợp đó chương trình hoạt động, chứng minh nó là trình biên dịch không phải thư viện đã thay đổi –

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