18

I'm going through the full tutorial at cplusplus.com, coding and compiling each example manually. Regularly, I stumble upon something that leaves me perplexed.

I am currently learning this section: http://www.cplusplus.com/doc/tutorial/structures/ . There are some subtleties that could easily be overlooked by only reading the tutorial. The advantage of typing everything by hand is that such details do stand out.

In the above page, there are two sample programs. One has this line:

stringstream(mystr) >> yours.year;

The other one has this line:

(stringstream) mystr >> pmovie->year;

What I don't understand is the difference (if any) between type (myVar) = x; and (type) myVar = x;.

I am not doing the whole tutorial in sequential order. I checked but didn't find this addressed anywhere, though I may have missed it.

  • Is there a difference?
  • Is there a preferred way to do it one way rather than the other?
StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
augustin
  • 14,373
  • 13
  • 66
  • 79
  • 12
    it's not a function, but a class. the first call is a constructor call, the second one is a conversion operator call. – KeatsPeeks Aug 14 '10 at 17:31

2 Answers2

59

There is no difference between type(x) and (type)x. These two are completely equivalent. Most people prefer type(x) for classes and (type)x for non-class types, but that's purely up to one's own choice. Both call constructors for classes with one argument x.

The preferred way for classes is type(x), because this allows passing more than one argument to the constructor, as in type(x, y). Trying to apply the other form, (type)x, y will not work: It casts x, and then applies the comma operator and evalutes y in isolation. Parentheses like (type)(x, y) do not help: This will evaluate x and y in isolation using the comma operator and then cast y to type.

For non-class types, such a cast is often too powerful. C++ has static_cast<type>(x) for roughly doing the reverse of an implicit conversion (such as casting base classes to derived classes and casting void* to another pointer), which often is what fits in. See When should static_cast, dynamic_cast and reinterpret_cast be used?.

stringstream is not a function, though. Doing function(x) will call it the function, but doing (function)x is illegal, beause there are two expressions next to each other, with no operator in between.


For those who don't believe this answer, and downvote it on gut feeling, please consult the Standard at 5.2.3/1

A simple-type-specifier (7.1.5) followed by a parenthesized expression-list constructs a value of the specified type given the expression list. If the expression list is a single expression, the type conversion expression is equivalent (in definedness, and if defined in meaning) to the corresponding cast expression (5.4).

Community
  • 1
  • 1
Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
  • I wouldn't say it's just a metter of coding style: one is a cast the other is a constructor call, they're two different concepts. – nico Aug 15 '10 at 11:14
  • 2
    @nico it's not a matter of opinion. The Standard defines what is equivalent and what is not. It defines these two as equivalent, so it's up to coding styles to say what is used in what case. – Johannes Schaub - litb Aug 15 '10 at 11:17
  • They generate equivalent results, that does not mean they are the same thing, and in fact the standard does not say they are the same, just that their results is equivalent. Probably a matter of semantics, but I reckon this question is about semantics. – nico Aug 15 '10 at 11:38
  • 5
    This is not neuroscience: These issues are exactly specified by a Standard, and what people think about the language does not matter for the semantics of it. Apparently you have already made up your mind about this issue, which is a pity, but i have no desire to change it. I have better ways to spend my time to :) – Johannes Schaub - litb Aug 15 '10 at 12:02
  • 2
    @nico oh i was kidding halfway. It's sunday, time for some fun. But seriously, the Standard says two things are equivalent - i think that's a clear statement. I don't see it open to interpretation. I would be glad if you could show a code example that clearly shows where behavior is different between these two forms. I think i agree that they are *lexically* different, much the same that `15` and `0xf` are "different", but how does this matter? It's completely up to one's own choice whether you type it in decimal or hexadecimal. – Johannes Schaub - litb Aug 15 '10 at 12:44
  • 1
    @Johannes Schaub - litb: Maybe we're just saying the same thing and we're just not understanding each other. I do agree: the outcome of the two statement is the same. What I'm saying is that in one case you have a cast, i.e. you have an object of class x and you "transform" it into class y; in the other case you call a constructor, so you "generate" an object of class y. At the end of the day you end up having an object of class y and we're all happy about that, but one syntax is used to cast the other to create. – nico Aug 15 '10 at 12:51
  • 3
    @nico the point that i want to make is that both are casts, though. Both call constructors for classes, and do not for non-classes. You can say int(some_pointer) as well as (int)some_pointer. I can see no value in saying that one form would call a constructor while the other would be a cast, because it suggests some difference that doesn't exist. There is no construtor call in int(some_pointer). I may call `10` a "creation" and `0x10` a "transformation" of a decimal because of the cute looking syntax, but that does not change anything about the *semantics* of it. It's completely subjective. – Johannes Schaub - litb Aug 15 '10 at 13:15
  • Ok, maybe there's something really obvious I'm missing, but I believe that in `stringstream(mystr) >> yours.year;`, mystr does not exist before the call and has type `stringstream`. In `(stringstream) mystr >> yours.year;`, on the other hand, mystr exists before the call, it has type `string` and it is being temporarily converted to a `stringstream`. – nico Aug 15 '10 at 13:53
  • @nico both are strings in the linked cplusplus.com tutorial. `stringstream` has a constructor that looks like `stringstream(std::string const&)`, and both forms use that to create a temporary stringstream object. If `mystr` would be a `stringstream` already, `stringstream(mystr)` would try to copy it into a temporary object, which would fail because streams cannot be copied. I think what you might think of is `stringstream mystr; mystr >> yours.year;`, which will create a new `mystr` stringstream variable and try to extract something out of it. – Johannes Schaub - litb Aug 15 '10 at 14:10
  • Notice that because of the syntax, `int(n);` is equivalent to `int n;`, because there are parentheses allowed to influence the meaning of the type of a variable (like, `int(*n)();` is a function pointer to a int-returning function, but `int*n();` is an `int*`-returning function). In `int(n);` these parentheses are redundant, but do not make the construct a non-declaration, thus it's parsed as a declaration. A construct like `int(n) >> 2;` however, cannot be parsed as a declaration anymore, and is parsed as a cast followed by a shift. Similarly, `(int(n));` would be a cast of `n` to `int`. – Johannes Schaub - litb Aug 15 '10 at 14:12
  • In other words, the function `void f() { int(n) + 0; }` is invalid, as is `void f() { (int)n; }`, because n does not exist yet, but is tried to cast to `int`. And `void f() { int n = 0; int(n); }` is invalid too, as is `void f() { int n = 0; int n; }`, because it tries to define `n` another time. C++'s syntax is screwed up like this :) I understand now what you meant when you said that the result is equal but the semantic is different, though. I hope it's clearer now. Cheers :) – Johannes Schaub - litb Aug 15 '10 at 14:24
  • ok, now I understand what you meant too. communication problems :) – nico Aug 15 '10 at 15:15
  • 13
    Who the *hell* is down-voting this answer? – GManNickG Aug 15 '10 at 18:55
  • +1 for accurately describing the situation, though the big "why?" is still there - just seems weird to me that `(Type_A)var_b` doesn't invoke `Type_B::operator Type_A()` in preference to e.g. `Type_A(const Type_B&)`... – Tony Delroy May 26 '11 at 03:10
  • @Tony: One intuitive way to think about it is that calling a conversion constructor is "easier" than calling a conversion operator. The conversion operator has to call a constructor somewhere inside of it, and there's a constructor directly available for that type, so why not use that? – John Calsbeek Jun 04 '11 at 13:23
  • @John: well, given return value optimisation is not generally guaranteed, that makes as much sense as anything I've heard... cheers. – Tony Delroy Jun 04 '11 at 14:06
4

The page you cite is not what I would consider a authority on C++ in general.

Anyway,

(stringstream) mystr >> pmovie->year;

casts a std::string to a std::stringstream object. This is a C-style cast. Rather dangerous if you don't know what you are doing. This would create a stringstream object and the value is extracted to pmovie->year next.

stringstream(mystr) >> yours.year;

Creates an anonymous std::stringstream object and initializes it with mystr and then the value is extracted to pmovie->year. The object vanishes at the end of its lexical scope which in this case would be the ; at the end of the line.

Not much of a difference (as others have noted so far) among the two w.r.t class objects.

On the other hand, with identifiers (of functions/macros) this gets tricky: function (myVar) = x; works irrespective of whether function is an actual function or a macro. However, (function) (myVar) = x; only works for real functions.

Some standard library identifiers are allowed to have both forms (most notably the tolower and friends) and therefore if you want to invoke the function always then you should go for the former.

dirkgently
  • 108,024
  • 16
  • 131
  • 187
  • 1
    whats wrong with cplusplus.com? I use their reference all the time. – rubenvb Aug 14 '10 at 18:04
  • 1
    It is a personal opinion, of course. And the OP's question is a case in point. – dirkgently Aug 14 '10 at 18:07
  • 1
    Your answer suggests there is a difference between both forms, but doesn't detail on that difference. It also suggests the ill-formed expression `(function) myVar` would be valid (as opposed to `(function)(myVar)`). – Johannes Schaub - litb Aug 15 '10 at 11:26
  • Thanks for the replies. I'm glad I asked. I understand much better now. Regarding cplusplus.com: what online C++ tutorial alternative would you recommend? I just stumbled upon http://www.learncpp.com/ which seems more complete. I also use cplusplus.com for it std library reference: http://www.cplusplus.com/reference/ . If you know a better C++ std library reference web site, let me know as well. – augustin Aug 16 '10 at 07:41
  • 2
    As reference: I prefer the SGI STL web site and MSDN references (though the examples are downright ugly at times). Try a book (something written Stroustrup) is generally accepted as a good start. – dirkgently Aug 16 '10 at 10:02