Jump to content
  • Advertisement
Sign in to follow this  
Wolfdog

C++ and arguments

This topic is 4175 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

First off I consider myself an extreamly competant programmer and I have a very good grasp of C and C++ aswell. That is the reason that I am so embaressed to ask but I know from reading hundreds of posts on this fourm that there are guys here that collectivly know everything about everything[smile]. I stumbeled across a piece of code that did something like: (this has been simplified)
TestFunction("Text",5);
There is nothing at all odd about this function call, until I noticed that the function definition was as follows:
class TestObj
{
  public:
    TestObj( int value ) {};
};
 
int TestFunction ( const std::string &String, const TestObj Object );
Now I realize that I have missed a huge part of C++. It appears that objects are constructed when there is a possible constructor and the argument is a const object. I have always in the case I wanted to do the functionaly same thing I would construct them in the function call (although this works with non const arguments aswell). So why is the C++ compiler constructing these objects for me, in a way I feel that I should be forced to create them myself? Is this just for code readability or is there actually something slightly different about this?

Share this post


Link to post
Share on other sites
Advertisement
When matching a function with an argument list, the C++ compiler is allowed to transform each argument into a temporary value of a related type (only one transform per argument is possible, but any number of arguments may be). If several transformations are possible, it chooses the one which transforms the fewest arguments—if there's a tie, it complains about ambiguity.

This allows you to call cos(float) on an integer, or display(const std::string &) on "Hello world" (which is a const char*). The ambiguity problem appears with std::sqrt(1) and converting the integer to either float or double.

A transformation from type A to B is possible if and only if one of the following is true:

  • A and B are basic types with a conversion defined (such as int and float).

  • B is a reference type to a superclass of type A, or B is a pointer to a superclass of the type pointed to by A, and that superclass appears only once in the hierarchy above A (so there is no ambiguity).

  • B is a void* pointer and A is a pointer.

  • B is a pointer, and A is an array of that same type.

  • B has a constructor from an A or const A&, and that constructor is not defined as explicit.

  • A defines an operator B(), and that operator is not defined as explicit.


It's late, so I may have forgotten a few.

Share this post


Link to post
Share on other sites
Quote:
Original post by Wolfdog
I have a very good grasp of C and C++ aswell.

I wouldn't say that if you weren't already aware of these conversion opportunities.

Quote:

int TestFunction ( const std::string &String, const TestObj Object );
Now I realize that I have missed a huge part of C++. It appears that objects are constructed when there is a possible constructor and the argument is a const object.


This has little to do with const -- "std::string String" would've allowed this code to work just fine as well. "const T&" allows the construction of a temporary value of type T for passing if the argument wasn't actually of type T, this is the one thing that isn't possible with the non-const "T&" (barring an conversion-to-reference operator at any rate)

Quote:
So why is the C++ compiler constructing these objects for me, in a way I feel that I should be forced to create them myself? Is this just for code readability or is there actually something slightly different about this?


Care to say why you feel that you should be forced to do this?

Explicitness? Seems like it'd just be explicit line noise here. std::strings and string literals are both "Strings", what's wrong with treating them the same way from the same function with the same passing sematics? Seems like the explicit conversions here would be a waste of screen real estate.

Safety? How? String-literal to std::string conversion is safe. For classes where such constructor provided converisons wouldn't be safe, we have the explicit keyword:

struct foo { foo ( int ) {} };
struct bar { explicit bar ( int ) {} };

void f_foo( foo ) {}
void f_bar( bar ) {}

void example() {
f_foo( 42 ); // OK
f_bar( 42 ); // Compile Error

f_foo( foo(42) ); //OK
f_bar( bar(42) ); //OK
}

Share this post


Link to post
Share on other sites
Quote:
Original post by MaulingMonkey
I wouldn't say that if you weren't already aware of these conversion opportunities.

Ouch!
I was only trying to express that I have been programming with these languages for quite a few years, obviously I do not know everything about either language. I guess I should take care when choosing my words.

Quote:
Original post by ToohrVyk
B has a constructor from an A or const A&, and that constructor is not defined as explicit.

I don't know how I have gone this long and missed that, oh well, learn something new everyday.

Quote:
Original post by MaulingMonkey
This has little to do with const...

I did not look deep enough into this, you have to use const if the object is to be passed by reference and this makes perfect sense because you would not want to pass the created object by reference. "A or const A&"

Quote:
Original post by MaulingMonkey
Care to say why you feel that you should be forced to do this?

Sure, but I must warn first that this is my personal opinion and I may not have all the facts that the C++ Standards Committee may have had when they decided to add this.

The reason I feel this way is as you said 'Explicitness'. Although in the case that std::string and const char * could potentially be both "Strings", const char * could also be an array of bytes that describe something entirely different. In the case that they are two obviously different types the code could be somewhat confusing:


class Bike;
struct Tire { Tire( float diameter }; };

Bike BuildBike (const Tire &Tire1, const Tire &Tire2);

// Questionable - What is 4.0 when building a bike
Bike newBike = BuildBike(4.0f,4.0f);

// Less Questionable - Although what is a 4.0 when building a tire
Bike newBike = BuildBike(Tire(4.0f),Tire(4.0f));



This may not be the best example because it only proves odd at first glance but I think it does a much better job at describing what is happening. Now again as you said 'Safety', obviously there is a safety issue because the explicit keyword was created. All of the other transformations that ToohrVyk listed make perfect sense where and why, this one just seems only good for cleaning up some code.

Ofcourse I have absolutly nothing against this type of argument transformation I am just taking a deep look at something new to me. Who knows maybe tomorrow I will decide it is very useful, I have only known about this for one day.

Share this post


Link to post
Share on other sites
Quote:
Original post by Wolfdog
Quote:
Original post by MaulingMonkey
I wouldn't say that if you weren't already aware of these conversion opportunities.

Ouch!

Meh. C++ is an insanely complex language -- and a dangerous-by-default one on top of that. My point is merely that you'd do well to have more prudence -- it's not so much the words that matter so much as the implications. Even I, one of the more knowledgeable people about the C++ language in this community, treat my knowledge of the language with guarded pessimism, for as much as I know about C++, I also know how much I don't know about C++.

And with this dangerous language, knowing what you don't know is almost as important as knowing what you do know. Maybe even more important.

Compared to Koeing lookup, sequence points, template specialization, dependant typenames, pointer-to-members, the details of integral promotion, operator chaining, and goodness knows what other examples I've missed thinking of, this conversion stuff isn't exactly on the "advanced topics" list, a list I'd expect familiarity with from one with "a very good grasp" on the language.

Quote:
I did not look deep enough into this, you have to use const if the object is to be passed by reference and this makes perfect sense because you would not want to pass the created object by reference. "A or const A&"


Right -- if you want non-const-reference access, you don't want implicit conversions hiding your variable changes by substituting a discarded temporary -- but it's otherwise sensible.

Quote:
Quote:
Original post by MaulingMonkey
Care to say why you feel that you should be forced to do this?

Sure, but I must warn first that this is my personal opinion and I may not have all the facts that the C++ Standards Committee may have had when they decided to add this.


Of course.

Quote:
The reason I feel this way is as you said 'Explicitness'. Although in the case that std::string and const char * could potentially be both "Strings", const char * could also be an array of bytes that describe something entirely different. In the case that they are two obviously different types the code could be somewhat confusing:

*** Source Snippet Removed ***


If it's questionable enough for there to be ambiguity, chances are more than likely it's ambiguous to the compiler too -- which means it'll generate a compiler error:

struct foo { foo( int ) {} };
struct bar { bar( int ) {} };

void f( foo ) {}
void f( bar ) {}

void example() {
f( 42 ); // MSVC2005 generates:
// error C2668: 'f' : ambiguous call to overloaded function
// could be 'void f(bar)'
// or 'void f(foo)'

f( foo(42) ); // OK
f( bar(42) ); // OK
}






While you will eventually run into some problems statements not doing what you think they do, most can be prevented with the use of explicit and such. The remaining problems caused by implicit conversions -- if your code is done right at any rate -- are outweighed as a whole by the problems you avoid by being able to actually read your code (by allowing you to spot errors before they become problems, or to spot problems before they lead to an entire symphony of cascading problems.)

Quote:
Ofcourse I have absolutly nothing against this type of argument transformation I am just taking a deep look at something new to me. Who knows maybe tomorrow I will decide it is very useful, I have only known about this for one day.


Hence my questioning -- whether it be to assuage misplaced fears or to explain the other half of the argument, or simply to chime in with what tools you have at your disposal for dealing with valid issues.

Share this post


Link to post
Share on other sites
Quote:
Original post by MaulingMonkey
... knowing what you don't know is almost as important as knowing what you do know. Maybe even more important.
I see your point and you're absolutely right.
Quote:

Hence my questioning...

This post has served the intended purpose, I now have a more useful apprehension of the subject topic. For that, I express my gratitude to both of you.

[Edited by - Wolfdog on June 15, 2007 3:01:23 PM]

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!