The following class compiles just fine:
class Time {
public:
Time(int h = 0, int m = 0, int s = 0); // constructor with default parameters
Time(); // default contructor
};
But when trying to use the default constructor one gets the following error:
timeMain.cpp: In function ‘int main()’:
timeMain.cpp:28:9: error: call of overloaded ‘Time()’ is ambiguous
28 | Time any;
| ^~~
Why is it possible to compile the class definition?
Why is there no error?
Why is there not at least a warning? There is not even anything with -Wall
.
I wanted to gather feedback before bringing this up as a feature request for gcc and clang. Maybe I'm missing something.
Thank you!
You appear to be focused on one specific case, a constructor that can be called with zero arguments. However, you can have a similar question about
class DateTime {
public:
DateTime (Date, int h = 0, int m = 0, int s = 0);
DateTime (Date);
};
We can make this even more complex with template declarations and concepts. This quickly becomes literally too complex to prove ambiguous. The compiler would need to somehow find a series of arguments A1, A2, ... An
for which at least two overloads are ranked the same.
If you want to bring this up as a feature request for gcc/clang, you should explain how the mechanism should work - which signatures T1, T2, ... Tn
and U1, U2, ... Un
are ambiguous and should produce a warning.
This does not answer the "why" as asked. However I can tell you how you can generate errors for all of the special member functions:
Every class one authors needs unit testing. And one very simple way to start unit testing for any class it to test that the 6 special member functions behave in the way you intend:
#include <type_traits>
// ...
static_assert( std::is_trivially_destructible_v<Time>);
static_assert(!std::is_default_constructible_v<Time>);
static_assert( std::is_nothrow_copy_constructible_v<Time>);
static_assert( std::is_nothrow_copy_assignable_v<Time>);
static_assert( std::is_nothrow_move_constructible_v<Time>);
static_assert( std::is_nothrow_move_assignable_v<Time>);
As written in the question, this is the set of tests that Time
currently passes.
Note that you can test if a type is or is not default constructible (with the preceding !
). You can also choose to test if the special member is noexcept or trivial, or decline to test on these characteristics. You have lots of choices.
Having these tests early in a type's development helps as you add data members and base classes to a type.
Having these tests means you can get errors now, with your existing compiler, instead of having to wait for a future language feature that might never arrive.
If desired, you can insert these unit tests right into the header under the class definition so that they get "run" every time the class is compiled. Or, if you would like to save a little compile time, you can put them into a separate unit test source. Your choice.
These tests easily copy/paste/customize to the next class you write.
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