Ambiguous Constructors (and functions)

What is the best method to get around ambiguity? The following is a problem that I can't seem to figure out how the real string class got around:

1
2
3
4
5
6
7
8
9
class String
{
public:
     // non-default variables shown for details
     String( const char & character );
     String( const char & character, int count );
     String( const char * string );
     String( const char * string, int start, int count );
}


...or...

1
2
3
4
5
6
7
class String
{
public:
     // non-default variables shown for details
     String( const char & character, int count = 1 );
     String( const char * string, int start = 0, int count = -1 );
}


that will allow me to differentiate between a wider array of constructor without trying to create one for every separate instance (which still doesn't always work especially if default arguments are defined)?
Last edited on
1
2
3
4
5
6
class String
{
  public:
     String( char c, int count = 1 );
     String( const char* str, int start = 0, int count = -1 );
};


should work.

no need to pass a char by const reference.

Also consider making both constructors explicit.
Exactly what does explicit entail?
Last edited on
What is an empty character constant?

It is neither a character (first constructor) nor a string (second constructor).

The empty string--""--uses the second constructor.

Basically, your line 10 does not make sense.

True, but the compiler none the less still reports an ambiguity error when I try to initialize String string1( '' ); as such. It seems to think that ( '' ) matches each:

1
2
3
String( const char & character, int count = 1 );
String( char c, int count 1 );
String( const char * string, int start = 0, int count = -1 );


And an empty character constant is error C2137 in reference to ( '' );
Last edited on
1
2
3
4
5
6
class String
{
  public:
     explicit String( char c, int count = 1 );
     explicit String( const char* str, int start = 0, int count = -1 );
};


First, "explicit" is only needed on constructors that can be called with exactly one parameter.
(Note all of the above can be, because all additional parameters are defaulted). Second, this
rule DOES NOT APPLY to the copy constructor; never make the copy constructor explicit.

To understand what explicit does, consider the following examples:

1
2
3
4
5
6
7
8
9
10
11
12
void foo( String s ) {
    // ...
}

// Without "explicit", all of the following lines compile:
String s( "Hello World!" );
String s2( 'X' );
foo( s );                      // Copy constructor is provided by compiler
foo( 'c' );                    // Implicitly calls first constructor to make a String from a char
foo( "Hello World" );    // Implicitly calls second constructor to make a String from a const char*

// With "explicit" on both constructors, only the first two lines above compile. 


So it seems like using explicit just makes you type more, right? Well, the reason for explicit is
because sometimes you don't want the compiler to perform the implicit construction. Why?
Because in a large program with a lot of classes, sometimes code compiles that you wouldn't
expect to compile thanks to the compiler diligently finding a series of implicit constructors it
can call.

For example, you have a function foo that takes an A as parameter. But somewhere in your
program you try to call it with a Z instance accidentally. You intended it to be a compile
error, but in fact, the compiler found that a B could be implicitly constructed from an A, and
a C implicitly constructed from a B, and an F from a C, a J from an F, etc, etc, until eventually
a Z could be made.

In small applications there isn't a whole lot of need for explicit. But for large apps it does make
sense. Since explicit only causes you to type a little bit more occasionally, I recommend that
programmers get into the habit of using explicit all the time. Only in certain very rare
circumstances do you want the implicit behavior (look at std::string for example).


What behavior do you want from your string class if the user instantiates a string with an empty character constant?
My compiler does not even allow empty character constants:

1
2
3
4
5
6
7
8
class Foo {
  public:
    Foo( char c, int = 0 ) {}
};

int main() {
   Foo f( '' );
}


g++ foo.cpp
foo.cpp:7:11: empty character constant


I didn't want it to compile actually, I just didn't want the complier to give me an ambiguous error in addition to the "empty character constant" error. =)

So as you demonstrated in your example, does that mean that using explicit would not allow a function to accept a instance of the String class as a parameter?

EDIT: Nevermind, I understand what your example is showing, the foo functions are accepting an instance of the String class and converting the char and char * into the appropriate String object. Thanks again jsmith (like the 3rd problem you've helped me with).

To explain, to further my programming knowledge and progressively improve my skills I'm trying to create my own String class that will do everything that <string.h> std::string provides (an possibly more). So far I've learned to accomplish quite a bit of its functionality but I occasionally run into problems (like the ambiguity) that become a pain to find ways around. I'd like to engineer my classes to accept input in many different forms and still be able to process the information, however C++ constructors do have their limitations.

Thanks again for all the help, it's greatly appreciated!
Last edited on
No problem. If you have any further questions/problems, you can also PM me.
Topic archived. No new replies allowed.