Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

what is the usecase for explicit (bool)

C++20 introduced explicit (bool) which conditionally selects at compile-time whether a constructor is made explicit or not.

Below is an example which I found here.

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

Can anyone tell me any other usecase for explicit (bool) other than using std::is_integral?

like image 273
NKAR Avatar asked Mar 12 '20 01:03

NKAR


People also ask

What is the explicit keyword?

The explicit keyword in C++ is used to mark constructors to not implicitly convert types. For example, if you have a class Foo − class Foo { public: Foo(int n); // allocates n bytes to the Foo object Foo(const char *p); // initialize object with char *p };

What is the need for explicit constructor?

The explicit function specifier controls unwanted implicit type conversions. It can only be used in declarations of constructors within a class declaration. For example, except for the default constructor, the constructors in the following class are conversion constructors.


2 Answers

The motivation itself can be seen in the paper.

There is a need to make constructors conditionally explicit. That is, you want:

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

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

The former is fine, those constructors are implicit. But the latter would be bad, those constructors are explicit. With C++17 (or C++20 with concepts), the only way to make this work is to write two constructors - one explicit and one not:

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&& );    
};  

These are almost entirely duplicated - and the definitions of these constructors would be identical.

With explicit(bool), you can just write a single constructor - with the conditionally explicit part of the construction localized to just the 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&& );   
};

This matches intent better, is much less code to write, and is less work for the compiler to do during overload resolution (since there are fewer constructors to have to pick between).

like image 60
Barry Avatar answered Oct 17 '22 21:10

Barry


Another possible usage I see is with variadic template:

It is generally good, by default, to have explicit for constructor with only one argument (unless the conversion is desired).

so

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

    // ...
};
like image 36
Jarod42 Avatar answered Oct 17 '22 22:10

Jarod42