apa usecase untuk eksplisit (bool)

24

C ++ 20 diperkenalkan secara eksplisit (bool) yang dipilih secara kondisional pada waktu kompilasi apakah konstruktor dibuat eksplisit atau tidak.

Di bawah ini adalah contoh yang saya temukan di sini .

struct foo {

  // Specify non-integral types (strings, floats, etc.) require explicit construction.

  template <typename T>

  explicit(!std::is_integral_v<T>) foo(T) {}

};

foo a = 123; // OK

foo b = "123"; // ERROR: explicit constructor is not a candidate (explicit specifier evaluates to true)

foo c {"123"}; // OK

Adakah yang bisa memberi tahu saya usecase explicit (bool)lain selain menggunakan std::is_integral?

NKAR
sumber
1
Salah satu contoh adalah menjadi jauh lebih mudah untuk mengimplementasikan konstruktor eksplisit bersyarat seperti untuk tuplefitur ini.
Praetorian
1
Bukan jawaban yang tepat, tetapi Anda juga bisa melihat motivasi dalam makalah yang memperkenalkannya: wg21.link/p0892
N. Shead
Contoh: itu (beserta konsepnya) mengurangi jumlah kelas dasar yang diperlukan untuk mengimplementasikan konstruktor penyalinan eksplisit kondisional yang disediakan dari 3 menjadi 0.
LF

Jawaban:

21

Motivasi itu sendiri dapat dilihat di koran .

Ada kebutuhan untuk membuat konstruktor eksplisit secara kondisional. Anda ingin:

pair<string, string> safe() {
    return {"meow", "purr"}; // ok
}

pair<vector<int>, vector<int>> unsafe() {
    return {11, 22}; // error
}

Yang pertama baik-baik saja, konstruktor itu implisit. Tapi yang terakhir akan buruk, konstruktor itu explicit. Dengan C ++ 17 (atau C ++ 20 dengan konsep), satu-satunya cara untuk membuat karya ini adalah dengan menulis dua konstruktor - satu explicitdan satu tidak:

template <typename T1, typename T2>
struct pair {
    template <typename U1=T1, typename U2=T2,
        std::enable_if_t<
            std::is_constructible_v<T1, U1> &&
            std::is_constructible_v<T2, U2> &&
            std::is_convertible_v<U1, T1> &&
            std::is_convertible_v<U2, T2>
        , int> = 0>
    constexpr pair(U1&&, U2&& );

    template <typename U1=T1, typename U2=T2,
        std::enable_if_t<
            std::is_constructible_v<T1, U1> &&
            std::is_constructible_v<T2, U2> &&
            !(std::is_convertible_v<U1, T1> &&
              std::is_convertible_v<U2, T2>)
        , int> = 0>
    explicit constexpr pair(U1&&, U2&& );    
};  

Ini hampir seluruhnya digandakan - dan definisi konstruktor ini akan sama.

Dengan explicit(bool), Anda bisa menulis konstruktor tunggal - dengan bagian eksplisit kondisional dari konstruksi dilokalkan ke explicit-specifier:

template <typename T1, typename T2>
struct pair {
    template <typename U1=T1, typename U2=T2,
        std::enable_if_t<
            std::is_constructible_v<T1, U1> &&
            std::is_constructible_v<T2, U2>
        , int> = 0>
    explicit(!std::is_convertible_v<U1, T1> ||
        !std::is_convertible_v<U2, T2>)
    constexpr pair(U1&&, U2&& );   
};

Maksud kecocokan ini lebih baik, lebih sedikit kode untuk ditulis, dan lebih sedikit pekerjaan yang harus dilakukan oleh kompiler selama resolusi kelebihan (karena ada lebih sedikit konstruktor yang harus dipilih).

Barry
sumber
1
C ++ 20 juga menyediakan kemampuan untuk mengubah enable_if_tbagian menjadi kendala yang lebih cantik dan sederhana, mungkin menggunakan konsep. Tapi bukan itu inti dari pertanyaan ini.
aschepler
2

Kemungkinan penggunaan lain yang saya lihat adalah dengan template variadic:

Secara umum, baik untuk memiliki explicitkonstruktor dengan hanya satu argumen (kecuali konversi diinginkan).

begitu

struct Foo
{
    template <typename ... Ts>
    explicit(sizeof...(Ts) == 1) Foo(Ts&&...);

    // ...
};
Jarod42
sumber
0

Saya dapat melihat use case untuk mensyaratkan secara explicitkondisional ketika input mungkin berupa tipe tampilan (pointer mentah, std::string_view) yang akan ditampung oleh objek baru setelah panggilan (hanya menyalin tampilan, bukan apa yang merujuk, tetap bergantung pada masa pakai objek yang dilihat), atau mungkin tipe seperti nilai (mengambil kepemilikan salinan, tanpa dependensi masa pakai eksternal).

Dalam situasi seperti itu, penelepon bertanggung jawab untuk menjaga objek yang dilihat tetap hidup (callee memiliki pandangan, bukan objek asli), dan konversi tidak boleh dilakukan secara implisit, karena itu membuatnya terlalu mudah untuk objek yang secara implisit dibuat untuk hidup lebih lama dari objek yang dilihatnya. Sebaliknya, untuk tipe nilai, objek baru akan menerima salinannya sendiri, jadi meskipun salinan itu mahal, itu tidak akan membuat kode salah jika konversi implisit terjadi.

ShadowRanger
sumber