Sign in to follow this  
dave

'Explicit' Keyword and Implicit Conversions

Recommended Posts

Hi, I'm going to write my own smart pointer class for educational purposes. I'm hoping that someone can help me with: 1) What does the explicit keyword do and when is it used? 2) What is an implicit conversion? Thanks in advance ace

Share this post


Link to post
Share on other sites
A constructor taking a single parameter is a conversion constructor.

struct Foo
{
Foo(int) {};
};

void Func(Foo) {};

Func(23); // Implicit conversion from int to Foo via Foo(int)
Func(Foo(23)); // Equivalent


Marking such a constructor explicit disables the implicit conversion.

struct Foo
{
explicit Foo(int) {};
};

void Func(Foo) {};

Func(23); // Flagged as an error
Func(Foo(23)); // Works


However, note that only one (implicit) user-defined conversion is performed.

struct Foo
{
Foo(int) {};
};

struct Bar
{
Bar(Foo) {}
};

void Func(Bar) {};

Func(23); // Error, requires two implicit user-defined conversions.
Func(Foo(23)); // Works, only one implicit conversion.


On the other hand, as many built-in type conversions (e.g. bool to int to double) as necessary will be perfmored.

struct Foo
{
Foo(double) {};
};

void Func(Foo) {};

Func(true); // Ok bool -> int -> double -> Foo


It can get tricky though

struct Foo
{
Foo(int) {};
};

void Func(Foo) {}
void Func(double) {}

Func(5); // Calls Func(Foo), Foo(int) is a better match than Func(double)


Of course, if there are multiple conversion paths, the compiler will complain about the ambiguity and your code won't compile.

struct Foo;

struct Bar
{
Bar() {};
Bar(Foo);
};

struct Foo
{
operator Bar() { return Bar(); }
};

Bar::Bar(Foo) {}

void Func(Bar) {};

int main()
{
Func(Foo()); // Error, use Bar::Bar(Foo) or Foo::operator Bar() ?
}


Making the conversion constructor explicit does fix that.

struct Foo;

struct Bar
{
Bar() {};
explicit Bar(Foo);
};

struct Foo
{
operator Bar() { return Bar(); }
};

Bar::Bar(Foo) {}

void Func(Bar) {};

int main()
{
Func(Foo()); // Will use Foo::operator Bar();
}


Idem for the overloaded Func()

struct Foo
{
explicit Foo(int) {};
};

void Func(Foo) {}
void Func(double) {}

Func(5); // Calls Func(double)
Func(Foo(5)); // Calls Func(Foo)



Considering that implicit conversion does create a temporary variable, it can be a good thing to provide appropriate overloads - particularly in operators, to bypass the conversion and directly do the right thing (that's why std::string has char* overloads on operator +(), for example)

struct Foo
{
Foo(int);
Foo(double);
};

Foo operator+(const Foo&, const Foo&); // (A)
Foo operator+(int, const Foo&); // (B)
Foo operator+(const Foo&, int); // (C)

Foo f;
f = f+5; // calls (C) operator+(f, 5); -> no temporary
f = f+5.0; // calls (A) operator+(f, Foo(5.0)); -> temporary


A side effect of explicit is that it disables the 'copy-initialization' syntax.

struct Foo
{
Foo(int) {}
};

struct Bar
{
explicit Bar(int) {};
};

Foo f = 5; // OK.
Foo g(5); // OK.
Bar b = 5; // Boom.
Bar c(5); // OK.


Copy-initialization also requires the copy-constructor (if user-defined) to be accessible (e.g. public), by the way, even though the compiler is allowed to optimize it away (which is significant since this is an optimization which can change a program's semantics if the copy-constructor happens to have side-effects).

[Edited by - Fruny on November 23, 2004 7:43:21 PM]

Share this post


Link to post
Share on other sites
Quote:
Original post by ace_lovegrove
So, fruny, in ur first example, what exactly happpens to the 23a dn how on earth can that be converted to and object type?

ace


Think of it this way: if an object has a constructor that takes only one argument then anywhere that expects an instance of the object you can instead use the value the constructor expects. In this case Foo has a constructor that takes an int and nothing else, so any time you would ordinarily use a Foo you can use an int instead.

Share this post


Link to post
Share on other sites
Quote:
So, fruny, in ur first example, what exactly happpens to the 23a dn how on earth can that be converted to and object type?

As he stated: "A constructor taking a single parameter is a conversion constructor." The constructor Foo(int) is silently called to construct a new object of type Foo, using existing data of type int.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this