2015-11-04 14 views
9

Tôi đã viết một số mã S s; ... s = {};, hy vọng nó sẽ kết thúc giống như S s = {};. Tuy nhiên nó không. Ví dụ sau tái tạo các vấn đề:Độ phân giải quá tải: gán dấu ngoặc rỗng

#include <iostream> 

struct S 
{ 
    S(): a(5) { } 
    S(int t): a(t) {} 

    S &operator=(int t) { a = t; return *this; } 
    S &operator=(S const &t) = default; 

    int a; 
}; 

int main() 
{ 
    S s = {}; 

    S t; 
    t = {}; 

    std::cout << s.a << '\n'; 
    std::cout << t.a << '\n'; 
} 

Đầu ra là:

5 
0 

Câu hỏi của tôi là:

  1. Tại sao operator=(int) chọn ở đây, thay vì "mơ hồ" hoặc một trong những khác ?
  2. Có cách giải quyết gọn gàng, không thay đổi S không?

Mục đích của tôi là s = S{};. Viết s = {}; sẽ thuận tiện nếu nó hoạt động. Tôi hiện đang sử dụng s = decltype(s){}; tuy nhiên tôi muốn tránh lặp lại loại hoặc tên biến.

+0

'{}' to 'int' là chuyển đổi nhận dạng ([\ [over.ics.list \]/9] (http://eel.is/c++draft/over.ics.list#9)). '{}' đến 'S' là một chuyển đổi do người dùng định nghĩa ([\ [over.ics.list \]/6] (http://eel.is/c++draft/over.ics.list#6)). Tôi thề tôi đã thấy một câu hỏi rất giống nhau ngày hôm qua ... –

+0

@ T.C. nếu vậy, tôi đoán tôi có thể đã tiết kiệm cho mình một số thời gian phát triển bằng cách đọc thêm câu hỏi ngày hôm qua! –

+0

Tôi dường như đã phạm sai lầm tương tự ở nhiều nơi trong mã của tôi ... ví dụ: Tôi thường sử dụng 'void clear() {* this = {}; } 'với mục đích khôi phục một đối tượng thành trạng thái khởi tạo giá trị của nó –

Trả lời

8

Tại sao chọn operator=(int) chọn ở đây, thay vì "mơ hồ" hoặc khác?

{} to int là chuyển đổi nhận dạng ([over.ics.list]/9).{} đến S là chuyển đổi do người dùng xác định ([over.ics.list]/6) (về mặt kỹ thuật, đó là {} đến const S& và đi qua [over.ics.list]/8 và [over.ics.ref] trước khi quay lại [over.ics. danh sách]/6).

Chiến thắng đầu tiên.

Có cách giải quyết gọn gàng không?

Biến thể của thủ thuật std::experimental::optional kéo để làm cho t = {} luôn làm cho t trống. Điều quan trọng là tạo operator=(int) một mẫu. Nếu bạn muốn chấp nhận int và chỉ int, sau đó nó trở thành

template<class Int, std::enable_if_t<std::is_same<Int, int>{}, int> = 0> 
S& operator=(Int t) { a = t; return *this; } 

trở ngại khác nhau có thể được sử dụng nếu bạn muốn kích hoạt chuyển đổi (bạn có lẽ cũng muốn lấy tham số biến trong trường hợp đó).

Vấn đề là bằng cách làm cho loại tác tử bên phai của một tham số mẫu, bạn chặn t = {} từ việc sử dụng quá tải này - bởi vì {} là một bối cảnh phi suy luận.

... mà không thay đổi S?

template<class T> T default_constructed_instance_of(const T&) { return {}; } và sau đó s = default_constructed_instance_of(s); đếm?

+0

Heh, liên quan đến dòng cuối cùng tôi đã đi với' template void reset (T & t) {t = T {}; } ' –

-1

Trước hết, trường hợp không liên quan đến phiên bản "int" của toán tử gán, bạn chỉ có thể xóa nó. Bạn thực sự có thể xóa toán tử gán khác vì nó sẽ được trình biên dịch tạo ra. IE loại kiểu này sẽ tự động nhận các nhà xây dựng sao chép/di chuyển và toán tử gán. (Tức là họ không bị cấm và bạn chỉ lặp lại những gì các trình biên dịch hiện tự động với ký hiệu rõ ràng)

Các trường hợp đầu tiên

sử dụng bản sao khởi:

S s = {};  // the default constructor is invoked 

Đó là một bài đăng chuyển nhượng bản sao -construction, nhưng trình biên dịch tối ưu hóa các trường hợp đơn giản như vậy. Bạn nên sử dụng hướng khởi thay vì:

S s{};  // the default constructor is invoked (as you have it) 

Lưu ý, bạn cũng có thể viết:

S s;   // the default constructor is invoked if you have it 

Trường hợp thứ hai

Những gì bạn nên viết là trực tiếp khởi của phía bên phải của nhiệm vụ sao chép:

t = S{}; 

Ký hiệu này sẽ gọi hàm khởi tạo mặc định (nếu có) hoặc khởi tạo giá trị cho các thành viên (miễn là loại này là tổng hợp). Dưới đây là thông tin có liên quan: http://en.cppreference.com/w/cpp/language/value_initialization

+0

tôi vẫn không nhận được trường hợp thứ hai của mình đang làm (như được viết) – pm100

+1

nếu tôi xóa phiên bản "int" của toán tử gán, sau đó đầu ra là '5 5' –

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