2015-10-07 20 views
12

Khi trả lời this question về cách cố gắng xây dựng một hàm tạo tham chiếu chuyển tiếp variadic chỉ nên được gọi nếu không có hàm tạo nào khác hợp lệ. Đó là, nếu có một:Nhà xây dựng dự phòng variadic - tại sao tính năng này hoạt động?

C(const char*, size_t) { }      // 1 
template <typename... T, ???> C(T&&...) { } // 2 

Chúng tôi muốn C c1{"abc", 2}; để gọi số (1), mặc dù việc chuyển đổi bắt buộc, nhưng C c2{1, 2, 3}; để gọi số (2), như (1) không thể áp dụng.

tôi đề xuất các giải pháp sau đây:

template <typename... T, 
      typename = std::enable_if_t<!std::is_constructible<C, T&&...>::value> 
      > 
C(T&&...) { } 

Và bằng cách đề nghị, ý tôi là, tôi đã thử nó và rất ngạc nhiên khi phát hiện ra rằng nó thực sự hoạt động. Nó biên dịch và thực hiện chính xác những gì tôi đã hy vọng cho cả gcc và clang. Tuy nhiên, tôi không hiểu giải thích tại sao lý do tại sao nó hoạt động hoặc thậm chí nếu nó thực sự là giả định để hoạt động và gcc và clang đều vừa đủ khả năng. Là nó? Tại sao?

+0

Dựa trên sự hiểu biết của tôi nếu một hàm tạo khác có sẵn cho 'C c1 {" abc ", 2};' hơn 'std :: enable_if_t' sẽ không có giá trị, và do đó tạo một cái gì đó như' template 'và SFINAE sẽ không tạo hàm tạo khuôn mẫu. –

+1

Đăng lại nhận xét của tôi về câu trả lời của bạn: [ví dụ được sửa đổi này] (http://melpon.org/wandbox/permlink/JM2U8pxxvguewhd1) cho thấy đối số mặc định SFINAE xem * tất cả * hàm tạo của 'C', thậm chí cả các hàm được khai báo sau mẫu hàm tạo. – dyp

+0

Đối với những gì nó có giá trị, MSVC 2013 cũng chấp nhận cú pháp này dưới/W4 và in giống như gcc và clang với [ví dụ này] (http://coliru.stacked-crooked.com/a/1fe984a4ceb6e193), mặc dù nó * không * cảnh báo về nhiều hàm tạo mặc định và intellisense dường như không nghĩ rằng lệnh gọi thứ ba tới 'Foo :: Foo' là hợp lệ. (Một lần nữa, điều này là mặc dù VC++ 18 là tốt với nó) – jaggedSpire

Trả lời

10

Vấn đề với mã của bạn là chúng tôi vừa tạo ra is_constructible trong ngữ cảnh có câu trả lời sai là. Bất kỳ loại bộ nhớ đệm nào trong mã mẫu đều có khả năng dẫn đến lỗi - hãy thử in is_constructible trên cùng các thông số sau khi bạn gọi hàm khởi tạo! Nó có thể làm cho nó sai.

Live example cách có thể xảy ra sự cố. Lưu ý rằng yêu cầu bồi thường C không thể được xây dựng từ int&, mặc dù đã thực hiện trên dòng trước đó.

struct C { 
    C(const char*, size_t) {} 
    template <class... Ts, 
    typename = std::enable_if_t<!std::is_constructible<C, Ts&&...>::value> 
    > 
    C(Ts&&...) { } 
}; 

int main() { 
    int a = 0; 
    C x{a}; 
    std::cout << std::is_constructible<C, int&>{} << '\n'; 
} 

oops.

Tôi cho rằng đây có thể là vi phạm ODR - hai định nghĩa của is_constructible có các loại khác nhau tại các vị trí khác nhau? Hoặc có thể không.

Solution to the original problem that does not have this issue cũng được đăng.

+0

@dyp có thể. Tôi đã chỉ ra rằng giải pháp trình bày không thực sự hiệu quả. Tôi đoán tôi đã đi và giải quyết vấn đề ban đầu là tốt. ;) Ok, chia thành hai phần. – Yakk

+0

Tiêu chuẩn xác định điểm khởi tạo của đối số mặc định của mẫu ở đâu? T.C. dường như ngụ ý trong các ý kiến ​​cho OP rằng điều này là underspecified .. – dyp

+0

@dyp Chắc chắn, tôi có thể tin rằng rất nhiều điều chưa được quy định trong tiêu chuẩn. Vì vậy, hoặc là nó không hoạt động (bởi vì nó được khởi tạo đủ trễ để xem chính nó?), Hoặc nó hoạt động (và phá vỡ thế giới, vì nó nhận được câu trả lời sai)? Quan điểm của tôi là dựa vào 'is_constructible' để có được câu trả lời sai không phải là một giải pháp, nhưng một vấn đề ẩn trong vỏ bọc của một giải pháp. – Yakk

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