Hello and welcome to c plus weekly - I'm your host Jason Turner. I'm available for code reviews
and on-site training. Now in this episode I'm going to cover C++20's explicit (bool) specifier
we're going to see why you might want such a thing and see if it might be applicable to your code.
Okay so you may or may not be aware that you can mark single,
well you can mark constructors, with any number of parameters actually as 'explicit' or not.
so let's go ahead and look at what this means. Now I'm on GCC-trunk here with C++20 enabled and all
kinds of warnings and errors and everything. I'm going to create a new type. I'm going to
call it 's' because that's a handy name for a struct. I'm going to make this a templated type
and it's going to contain some sort of
thing. Now you might think of this like say, a strongly typed wrapper, or
some sort of a std::optional maybe, something like that and
you've got some desire to be able to convert this thing 's' to and from a Contained.
I've made a constructor that takes an r-value reference to the contained
thing and a constructor that takes a const l-value reference to the contained thing,
and I've got some sort of get member, shall we something like this; and now for convenience
sometimes you want to actually be able to have the compiler implicitly convert
this thing for you so a conversion operator might not be uncalled for here.
Now this might look kind of weird to you if you aren't familiar with the syntax but it says,
"If an object of type contained is required then I'm going to return a copy of my contained thing,"
and now we could do r-value or l-value reference qualified member functions for our operator
contained or something like that; let's stick with the version that we have right now for simplicity.
Now let's just pretend like we have a function here that wants an object of type
's' that contains an int. Because we have marked this function not explicit then it is
an implicit converting constructor for us. So I can call a function that expects an object of type
S(int) and pass it an int and it is going to automatically call this constructor for us. We
can in fact actually see that it's automatically calling this constructor for us because it's the
only line highlighted up here in compiler explorer and we do have our optimizations enabled at the
moment. If I have another function that expects an object of type int and I pass into other_func
an object of type S(int) (I need to give this a default constructor) then I'm passing in an object
of type S(int) to a function expecting an int, and it's actually calling this conversion operator
here for me. Now these conversions, implicit conversions, can actually be very expensive and
I personally think that std::string never should have had an implicit conversion from
a const character literal because it gets us in trouble all over the place with a bunch of
expensive copies. So, pretty much everyone agrees that these functions should be marked explicit
so that it avoids this potential problem of having
expensive conversions and copies happen all over the place. Now I want to do a quick aside and note
here that if I have the move constructor marked explicit it knows that it can't call that one
(sorry move constructor is a bad word) but the constructor by r-value reference, and so it calls
the constructor by l-value reference implicitly so we actually get a different constructor called
because I added explicit here. So let that one just kinda maybe mess with your head a little bit
but let's go ahead and make these explicit again and now we get a compile time error.
The compiler is saying "Hey, you can't do this implicit conversion because this is potentially
expensive and it reduces type safety and has all other potential problems." Now as of C++11 we can
also mark our converting constructors explicit so now we get compile time errors on both sides and I
would have to explicitly call .get right here and I would have to explicitly create an object here.
Now I don't honestly know what the original motivating intention is
for conditionally explicit constructors but I will say (and conversion operators),
I will say that I can see a case where you can make these things conditionally explicit
where if the contained type the conversion is cheap if the conversion is cheap or free
and does not cause a code bug then in C++ 20 you could do something like this -
using a typedef here or something like that would probably be, um you know,
smarter than what I'm doing right now, but if I have an int that I want to pass in or if I have
this thing then I can say this thing is explicit only in the case where it is containing something
that is not trivial so now it will work. Now I have implicit conversions to and from a trivially
contained type, but if I were to go back and have something that is not trivial in here
such as a string, now I'm going to get this compile time error because invalid initialization
of reference of type const string from expression for type string; note passing argument 1 here
because I have made this conditionally explicit on whether or not it is a
trivial type, then I cannot call it here. Now the error that I'm actually getting from the compiler
is kind of terrible because it's not telling me that it was because the constructor that it could
find, or the converting operator that could find, was conditionally explicit here so let's just for
the fun of it also try clang_trunk. Here we go - candidate not viable, no matching function;
candidate not viable, no known conversion. Okay it still isn't giving us a great error but that is a
use case for conditionally explicit constructors and conversion operators and C++ 20 makes this
very easy to actually use by using our explicit that takes a constexpr bool parameter to determine
whether or not these things should be explicit or implicit. I feel like this is a tool that
is probably going to be used very little in average code but might find some use cases like
my example here, in the case where something's trivial and it probably has a lot of use
in various standard library kinds of applications. So thanks for checking out
this episode of C++ Weekly I hope you enjoyed it and be sure to subscribe leave a comment!