Date: Sat, 10 Jan 2026 13:16:49 +0100
On 09/01/2026 19:49, Jonathan Wakely wrote:
>
>
> On Fri, 9 Jan 2026 at 17:19, David Brown via Std-Proposals <std-
> proposals_at_[hidden] <mailto:std-proposals_at_[hidden]>> wrote:
>
>
>
> On 09/01/2026 17:37, Steve Weinrich via Std-Proposals wrote:
> > Consider two methods:
> >
> > void interest (double arg1, bool minor);
> >
> > void interest (double arg1, int factor);
> >
> > The call: interest(5.5, 5); is ambiguous because 5 will freely
> convert
> > to a bool.
>
> No, it is not ambiguous - the best fit overload will be used. The
> rules
> for overload resolution are a bit complicated, but in this case the
> results are clear.
>
> <https://en.cppreference.com/w/cpp/language/overload_resolution.html
> <https://en.cppreference.com/w/cpp/language/overload_resolution.html>>
>
> >
> > I was wondering what y’all would think of narrowing this behavior by
> > this addition:
> >
> > void interest (double arg1, *explicit* bool minor);
> >
> > Potentially, this could be applied to all arguments:
> >
> > void *explicit* interest (double arg1, bool minor);
> >
>
> To me, a bigger issue is when you /don't/ have overloads :
>
>
> void interest (double arg1, bool minor);
>
> In this situation, the implicit conversion of an int to a bool for
> "interest(5.5, 5)" is likely to be an error on the programmer's side,
> but it is accepted without question by C++ compilers. To avoid this,
> some people use "strong bool" types that have much stricter implicit
> conversions, such as :
>
> class Bool {
> bool v;
> public :
> constexpr Bool(bool v) noexcept : v(v) {}
> Bool(auto v) = delete;
> explicit constexpr operator bool() const noexcept { return v; }
> };
>
> Then you can declare the function :
>
> void interest (double arg1, Bool minor);
>
> and use it with a bool type as the second argument, but not with
> another
> type that has an implicit conversion to bool.
>
>
> A class seems overkill here, I would use a scoped enum type:
>
> enum class InterestType { Minor, Major };
>
> This won't allow any explicit conversions.
Sure - a scoped enum is often a good alternative to a bool parameter.
But my point was that people do make and use strong bools precisely as a
way to stop implicit conversions. (People also make strong integer and
floating point types that don't have implicit conversions.)
The std::same_as<bool> constrained auto template is a neat solution for
when you don't want to make a specific type (or an enum class). The
only disadvantage I see compared to a hypothetical "explicit argument"
is if you already had an existing definition (in another file) of the
function with a simple "bool" parameter. The mangled function name for
"int foo(std::same_as<bool> auto)" called with a bool argument is not
the same as the mangled function name for "int foo(bool)". Still, if
you retrofitting a bit of call safety to existing code, you can always
leave the "int foo(bool)" declaration untouched and add a "int
foo(auto...) = delete".
>
> But it seems like a badly-designed overload set anyway, since you have
> one function name "interest" but the functions do two different things.
> It would probably be better if you had names that make what they do
> clearer, calc_interest_factor and calc_interest_major_minor or something
> (I have no idea what the "minor" argument means, so better names are
> probably possible).
>
>
I fully agree that overloads need to be designed carefully to avoid
confusion. I have no idea if the OP took the example from real code, or
it was just hypothetical.
>
>
> On Fri, 9 Jan 2026 at 17:19, David Brown via Std-Proposals <std-
> proposals_at_[hidden] <mailto:std-proposals_at_[hidden]>> wrote:
>
>
>
> On 09/01/2026 17:37, Steve Weinrich via Std-Proposals wrote:
> > Consider two methods:
> >
> > void interest (double arg1, bool minor);
> >
> > void interest (double arg1, int factor);
> >
> > The call: interest(5.5, 5); is ambiguous because 5 will freely
> convert
> > to a bool.
>
> No, it is not ambiguous - the best fit overload will be used. The
> rules
> for overload resolution are a bit complicated, but in this case the
> results are clear.
>
> <https://en.cppreference.com/w/cpp/language/overload_resolution.html
> <https://en.cppreference.com/w/cpp/language/overload_resolution.html>>
>
> >
> > I was wondering what y’all would think of narrowing this behavior by
> > this addition:
> >
> > void interest (double arg1, *explicit* bool minor);
> >
> > Potentially, this could be applied to all arguments:
> >
> > void *explicit* interest (double arg1, bool minor);
> >
>
> To me, a bigger issue is when you /don't/ have overloads :
>
>
> void interest (double arg1, bool minor);
>
> In this situation, the implicit conversion of an int to a bool for
> "interest(5.5, 5)" is likely to be an error on the programmer's side,
> but it is accepted without question by C++ compilers. To avoid this,
> some people use "strong bool" types that have much stricter implicit
> conversions, such as :
>
> class Bool {
> bool v;
> public :
> constexpr Bool(bool v) noexcept : v(v) {}
> Bool(auto v) = delete;
> explicit constexpr operator bool() const noexcept { return v; }
> };
>
> Then you can declare the function :
>
> void interest (double arg1, Bool minor);
>
> and use it with a bool type as the second argument, but not with
> another
> type that has an implicit conversion to bool.
>
>
> A class seems overkill here, I would use a scoped enum type:
>
> enum class InterestType { Minor, Major };
>
> This won't allow any explicit conversions.
Sure - a scoped enum is often a good alternative to a bool parameter.
But my point was that people do make and use strong bools precisely as a
way to stop implicit conversions. (People also make strong integer and
floating point types that don't have implicit conversions.)
The std::same_as<bool> constrained auto template is a neat solution for
when you don't want to make a specific type (or an enum class). The
only disadvantage I see compared to a hypothetical "explicit argument"
is if you already had an existing definition (in another file) of the
function with a simple "bool" parameter. The mangled function name for
"int foo(std::same_as<bool> auto)" called with a bool argument is not
the same as the mangled function name for "int foo(bool)". Still, if
you retrofitting a bit of call safety to existing code, you can always
leave the "int foo(bool)" declaration untouched and add a "int
foo(auto...) = delete".
>
> But it seems like a badly-designed overload set anyway, since you have
> one function name "interest" but the functions do two different things.
> It would probably be better if you had names that make what they do
> clearer, calc_interest_factor and calc_interest_major_minor or something
> (I have no idea what the "minor" argument means, so better names are
> probably possible).
>
>
I fully agree that overloads need to be designed carefully to avoid
confusion. I have no idea if the OP took the example from real code, or
it was just hypothetical.
Received on 2026-01-10 12:16:52
