Tại sao {} làm đối số hàm không dẫn đến sự mơ hồ?


20

Xem xét mã này:

#include <vector>
#include <iostream>

enum class A
{
  X, Y
};

struct Test
{
  Test(const std::vector<double>&, const std::vector<int>& = {}, A = A::X)
  { std::cout << "vector overload" << std::endl; }

  Test(const std::vector<double>&, int, A = A::X)
  { std::cout << "int overload" << std::endl; }
};

int main()
{
  std::vector<double> v;
  Test t1(v);
  Test t2(v, {}, A::X);
}

https://godbolt.org/z/Gc_w8i

Bản in này:

vector overload
int overload

Tại sao điều này không tạo ra lỗi biên dịch do độ phân giải quá tải mơ hồ? Nếu hàm tạo thứ hai bị loại bỏ, chúng ta sẽ nhận được vector overloadhai lần. Làm thế nào / bởi số liệu nào là intmột kết hợp tốt hơn rõ ràng hơn {}so với std::vector<int>?

Chữ ký của nhà xây dựng chắc chắn có thể được cắt bớt, nhưng tôi chỉ bị lừa bởi một đoạn mã tương đương và muốn đảm bảo không có gì quan trọng bị mất cho câu hỏi này.


Nếu tôi nhớ chính xác {}là một khối mã, gán 0 cho các biến - ví dụ: const char x = {}; được đặt thành 0 (null char), tương tự cho int, v.v.
Seti

2
@ Seti Đó là những gì {}có hiệu quả trong một số trường hợp đặc biệt, nhưng nói chung là không chính xác (đối với người mới bắt đầu, std::vector<int> x = {};công việc, std::vector <int> x = 0;thì không). Nó không đơn giản như " {}gán số không".
Max Langhof

Phải, nó không đơn giản như vậy, nhưng nó vẫn chỉ định số không - tôi nghĩ rằng beaviour này khá khó hiểu và không nên sử dụng thực sự
Seti

2
@ Seti struct A { int x = 5; }; A a = {};không gán số 0 theo bất kỳ nghĩa nào, nó xây dựng một Avới a.x = 5. Điều này là không giống nhau A a = { 0 };, không khởi tạo a.xthành 0. Số 0 không phải là vốn có {}, nó vốn có với cách mỗi loại được xây dựng mặc định hoặc khởi tạo giá trị. Xem ở đây , ở đâyở đây .
Max Langhof

Tôi vẫn nghĩ rằng giá trị được xây dựng mặc định là khó hiểu (yêu cầu bạn kiểm tra hành vi hoặc giữ nhiều kiến ​​thức mọi lúc)
Seti

Câu trả lời:


12

Nó nằm trong [over.ics.list] , nhấn mạnh vào tôi

6 Mặt khác, nếu tham số là lớp X không tổng hợp và độ phân giải quá tải cho mỗi [over.match.list] chọn một hàm tạo tốt nhất C của X để thực hiện khởi tạo một đối tượng loại X từ danh sách trình khởi tạo đối số:

  • Nếu C không phải là hàm tạo của danh sách khởi tạo và danh sách khởi tạo có một phần tử loại cv U, trong đó U là X hoặc một lớp có nguồn gốc từ X, chuỗi chuyển đổi ngầm định có Xếp hạng khớp chính xác nếu U là X hoặc xếp hạng Chuyển đổi nếu U có nguồn gốc từ X.

  • Mặt khác, chuỗi chuyển đổi ngầm định là chuỗi chuyển đổi do người dùng xác định với chuỗi chuyển đổi tiêu chuẩn thứ hai là chuyển đổi nhận dạng.

9 Mặt khác, nếu loại tham số không phải là một lớp:

  • [...]

  • nếu danh sách trình khởi tạo không có phần tử, trình tự chuyển đổi ẩn là chuyển đổi danh tính. [ Thí dụ:

    void f(int);
    f( { } ); // OK: identity conversion
    

    ví dụ cuối]

std::vectorđược khởi tạo bởi constructor và dấu đầu dòng in đậm coi đó là một converison do người dùng định nghĩa. Trong khi đó, đối với một int, đây là chuyển đổi danh tính, do đó, nó vượt qua thứ hạng của người đầu tiên.


Vâng, có vẻ chính xác.
Columbo ngày

Thật thú vị khi thấy rằng tình huống này được xem xét rõ ràng trong tiêu chuẩn. Tôi thực sự mong đợi nó sẽ mơ hồ (và có vẻ như nó có thể dễ dàng được chỉ định theo cách đó). Tôi không thể làm theo lý do trong câu cuối cùng của bạn - 0có loại intnhưng không phải loại std::vector<int>, làm thế nào mà một "như thể" viết theo bản chất "không được đánh dấu" của {}?
Max Langhof ngày

@MaxLanghof - Một cách khác để xem xét đó là đối với các loại không thuộc lớp, nó không phải là chuyển đổi do người dùng xác định theo bất kỳ độ dài nào. Thay vào đó, nó là một trình khởi tạo trực tiếp cho giá trị mặc định. Do đó một danh tính trong trường hợp này.
Người kể chuyện - Unslander Monica

Phần đó là rõ ràng. Tôi ngạc nhiên bởi sự cần thiết phải chuyển đổi do người dùng xác định std::vector<int>. Như bạn đã nói, tôi mong đợi "loại tham số kết thúc quyết định loại đối số nào" và {}"loại" (có thể nói) std::vector<int>không cần chuyển đổi (không nhận dạng) để khởi tạo a std::vector<int>. Tiêu chuẩn rõ ràng nói rằng nó có, vì vậy đó là, nhưng nó không có ý nghĩa với tôi. (Xin lưu ý bạn, tôi không cho rằng bạn hoặc tiêu chuẩn là sai, chỉ cố gắng dung hòa điều này với các mô hình tinh thần của tôi.)
Max Langhof

Ok, chỉnh sửa đó không phải là độ phân giải mà tôi hy vọng, nhưng đủ công bằng. : D Cảm ơn bạn đã dành thời gian!
Max Langhof ngày
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.