2017-09-18 13 views
7
template<class _Other1, 
    class _Other2, 
    class = enable_if_t<is_constructible<_Ty1, _Other1>::value 
        && is_constructible<_Ty2, _Other2>::value>, 
    enable_if_t<is_convertible<_Other1, _Ty1>::value 
      && is_convertible<_Other2, _Ty2>::value, int> = 0> 
    constexpr pair(pair<_Other1, _Other2>&& _Right) 
     _NOEXCEPT_OP((is_nothrow_constructible<_Ty1, _Other1>::value 
      && is_nothrow_constructible<_Ty2, _Other2>::value)) 
    : first(_STD forward<_Other1>(_Right.first)), 
     second(_STD forward<_Other2>(_Right.second)) 
    { // construct from moved compatible pair 
    } 

template<class _Other1, 
    class _Other2, 
    class = enable_if_t<is_constructible<_Ty1, _Other1>::value 
        && is_constructible<_Ty2, _Other2>::value>, 
    enable_if_t<!is_convertible<_Other1, _Ty1>::value 
      || !is_convertible<_Other2, _Ty2>::value, int> = 0> 
    constexpr explicit pair(pair<_Other1, _Other2>&& _Right) 
     _NOEXCEPT_OP((is_nothrow_constructible<_Ty1, _Other1>::value 
      && is_nothrow_constructible<_Ty2, _Other2>::value)) 
    : first(_STD forward<_Other1>(_Right.first)), 
     second(_STD forward<_Other2>(_Right.second)) 
    { // construct from moved compatible pair 
    } 

tiện ích tập tin cho VS 2017 dòng 206, _Other1 và _Other2 là các thông số, đây là std :: cặp 's func xây dựng, và chúng tôi đang sử dụng Other1 và Other2 để khởi "đầu tiên" và "thứ hai",

Tôi nghĩ rằng is_constructible là đủ, tại sao chúng tôi sử dụng is_convertible tại đây?
và nhân tiện, sự khác biệt giữa class = enable_if_t< ... ::value>enable_if_t< ... ::value,int> = 0 là gì?Tại sao 'is_convertible' ở đây trong <utility> std :: pair (STL)?

+0

Điều này là để triển khai [\ [pairs.pair \]/12] (http://eel.is/c++draft/pairs.pair#12.sentence-2). – cpplearner

Trả lời

9

Tôi nghĩ is_constructible là đủ, tại sao chúng ta sử dụng is_convertible đây?

Mục tiêu ở đây là xử lý đúng cách xây dựng explicit. Cân nhắc chỉ làm cũ và cố gắng để viết một wrapper (sử dụng REQUIRES đây để ẩn bất cứ cách tiếp cận để SFINAE bạn muốn):

template <class T> 
class wrapper { 
public: 
    template <class U, REQUIRES(std::is_constructible<T, U&&>::value)> 
    wrapper(U&& u) : val(std::forward<U>(u)) { } 
private: 
    T val; 
}; 

Nếu đó là tất cả chúng tôi đã có, sau đó:

struct Imp { Imp(int); }; 
struct Exp { explicit Exp(int); }; 

Imp i = 0; // ok 
Exp e = 0; // error 
wrapper<Imp> wi = 0; // ok 
wrapper<Exp> we = 0; // ok?!? 

Chúng tôi chắc chắn don không muốn cái cuối cùng sẽ ổn - điều đó phá vỡ kỳ vọng cho Exp!

Hiện tại, s_constructible<T, U&&> là đúng nếu có thể trực tiếp khởi tạo số T từ U&& - nếu T(std::declval<U&&>()) là biểu thức hợp lệ.

is_convertible<U&&, T>, mặt khác, kiểm tra xem nó có thể sao chép -initialize một T từ một U&&. Tức là, nếu T copy() { return std::declval<U&&>(); } hợp lệ.

Sự khác biệt là sau này không hoạt động nếu việc chuyển đổi là explicit:

+-----+--------------------------+------------------------+ 
|  | is_constructible<T, int> | is_convertible<int, T> | 
+-----+--------------------------+------------------------+ 
| Imp |  true_type   |  true_type  | 
| Exp |  true_type   |  false_type  | 
+-----+--------------------------+------------------------+ 

Nhằm tuyên truyền một cách chính xác minh bạch, chúng ta cần phải sử dụng cả hai đặc điểm với nhau - và chúng tôi có thể tạo meta-nét ngoài họ:

template <class T, class From> 
using is_explicitly_constructible = std::integral_constant<bool, 
    std::is_constructible<T, From>::value && 
    !std::is_convertible<From, T>::value>; 

template <class T, class From> 
using is_implicitly_constructible = std::integral_constant<bool, 
    std::is_constructible<T, From>::value && 
    std::is_convertible<From, T>::value>; 

hai đặc điểm là rời nhau, vì vậy chúng tôi có thể viết hai mẫu nhà xây dựng mà là chắc chắn không phải cả hai khả thi, trong đó một constructor là rõ ràng và người kia không phải là:

template <class T> 
class wrapper { 
public: 
    template <class U, REQUIRES(is_explicitly_constructible<T, U&&>::value)> 
    explicit wrapper(U&& u) : val(std::forward<U>(u)) { } 

    template <class U, REQUIRES(is_implicitly_constructible<T, U&&>::value)> 
    wrapper(U&& u) : val(std::forward<U>(u)) { } 
private: 
    T val; 
}; 

này mang lại cho chúng ta những hành vi mong muốn:

wrapper<Imp> wi = 0; // okay, calls non-explicit ctor 
wrapper<Exp> we = 0; // error 
wrapper<Exp> we2(0); // ok 

Đây là những gì thực hiện được làm gì ở đây - ngoại trừ thay vì hai meta-đặc điểm họ có tất cả các điều kiện viết ra explicit ly.

8

Đối với việc thực hiện [pairs.pair]/12:

constructor này sẽ không tham gia vào giải quyết tình trạng quá tải trừ khi is_­constructible_­v<first_­type, U1&&> là đúng và is_­constructible_­v<second_­type, U2&&> là đúng. Trình tạo là rõ ràng nếu và chỉ khi is_­convertible_­v<U1&&, first_­type> là sai hoặc is_­convertible_­v<U2&&, second_­type> là sai.

+0

Nhưng * tại sao * nó được cho là '' tường minh'' nếu một hoặc cả hai loại không chuyển đổi? –

+3

@ArneVogel Chuyển đổi ngầm định loại sang một loại khác, nơi chuyển đổi rõ ràng sẽ được cho phép nếu không cho kiểm tra 'std :: is_convertible_v'. – Rakete1111

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