2017-01-07 16 views
7

Giả sử tôi có một lớp mẫuForwarding hành vi tham khảo với các loại định

template <typename T> class foo; 
template <typename... Args> 
struct foo<std::tuple<Args...>> { 
    std::tuple<Args...> t; 
    foo(Args&&... args): t{std::forward<Args>(args)...} { } 
}; 

Tôi hiểu rằng trong trường hợp này Args&&... là tài liệu tham khảo rvalue, và tôi có thể có cũng giống như cũng bằng văn bản std::move thay vì std::forward.

Tôi cũng có thể có một constructor với sự tham khảo giá trị trái, như vậy

foo(const Args&... args): t{args...} { } 

Câu hỏi đặt ra là liệu nó có thể để có được những hành vi tương tự như với sự tham khảo chuyển tiếp, nhưng với nhiều loại nhất định? Lý do tôi muốn điều này là để tôi có thể sử dụng cú pháp như

foo bar({. . .}, {. . .}, . . ., {. . .}); 

này hoạt động nếu tôi xác định các nhà xây dựng foo(Args&&... args), nhưng không cho phép cho một kịch bản hỗn hợp, nơi tôi muốn khởi tạo một số trong những yếu tố thành viên tuple với brace-kèm theo initializer danh sách và có những người khác sao chép từ các trường hợp đối tượng từ trước.

+0

Đây không phải là "tham chiếu chuyển tiếp" nếu đó không phải là tham chiếu chuyển tiếp. (Tham chiếu chuyển tiếp yêu cầu đối số mẫu được suy luận.) –

Trả lời

2

Chắc chắn; có một cách ưa thích và đơn giản.

Cách ưa thích tôi sẽ trình bày chi tiết bên dưới. Đầu tiên là cách đơn giản: lấy theo giá trị.

template <typename... Args> 
struct foo<std::tuple<Args...>> { 
    std::tuple<Args...> t; 
    foo(Args... args): t{std::forward<Args>(args)...} { } 
}; 

Thực sự, hãy làm việc này. Chuyển tiếp được sử dụng để làm điều đúng nếu Args chứa tham chiếu.

Tính theo giá trị, hãy thêm một bước chuyển tiếp hoàn hảo, nhưng giảm yêu cầu về tình trạng quá tải theo cấp số nhân.


Đây là cách ưa thích. Chúng tôi nhập xóa xây dựng:

template<class T> 
struct make_it { 
    using maker=T(*)(void*); 
    maker f; 
    void* args; 
    // make from move 
    make_it(T&& t): 
    f([](void* pvoid)->T{ 
     return std::move(*static_cast<T*>(pvoid)); 
    }), 
    args(std::addressof(t)) 
    {} 
    // make from copy 
    make_it(T const& t): 
    f([](void* pvoid)->T{ 
     return *(T const*)(pvoid); 
    }), 
    args(std::addressof(t)) 
    {} 
    operator T()&&{return std::move(*this)();} 
    T operator()()&&{ return f(args); } 
}; 

Loại này sẽ xóa bản dựng bằng cách sao chép hoặc di chuyển.

template <typename... Args> 
struct foo<std::tuple<Args...>> { 
    std::tuple<Args...> t; 
    foo(make_it<Args>... args): t{std::move(args)()...} { } 
}; 

Nó không hoàn toàn minh bạch, nhưng nó gần như tôi có thể nhận được.

Cần có {{}} thay vì duy nhất. Đây là một chuyển đổi do người dùng xác định, vì vậy một chuyển đổi khác sẽ không được thực hiện hoàn toàn. Chúng ta có thể thêm một ctor phổ quát:'

// make from universal 
    template<class U> 
    make_it(U&& u): 
    f([](void* pvoid)->T{ 
     return std::forward<U>(*(U*)(pvoid)); 
    }), 
    args(std::addressof(u)) 
    {} 

mà hoạt động tốt hơn nếu chúng ta thêm một núm vú sfinae rằng U&& có thể được sử dụng để xây dựng ngầm T.

Nó có một số lợi thế, nhưng chúng rất nhỏ so với việc lấy giá trị. Ví dụ, trong C++ 17 loại không thể di chuyển có thể được chuyển tiếp hoàn hảo trong một số trường hợp.

+0

Tại sao có '+' đơn nhất áp dụng cho lambdas? Sự ép buộc rõ ràng đối với con trỏ dường như không cần thiết ở đây. –

+0

@yuri chủ yếu là thói quen? Tôi đoán sự tồn tại của Msvc bị hỏng làm cho nó là một ý tưởng tồi. – Yakk

+0

@Yakk Điều này thật tuyệt vời. Cảm ơn câu trả lời ưa thích. Tôi phải thú nhận, tôi không hiểu toàn bộ mẹo trong nháy mắt. Bạn có ý gì khi "thêm một sfinae teat rằng' U ​​&& 'có thể được sử dụng để xây dựng ngầm' T' "?Đó là chức năng duy nhất tôi yêu cầu ở đây. Có thể sử dụng trực tiếp với hàm tạo của 'foo', nếu nó được viết dưới dạng' template foo (U && ... u): t {std :: forward (u) ...} {} '? – SU3

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