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
?
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 };
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.
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).
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&&...);
// ...
};
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With