45

What is the difference between

(type)value

and

type(value)

in C++?

Xaver Kapeller
  • 49,491
  • 11
  • 98
  • 86
SMART_n
  • 1,070
  • 8
  • 10
  • 1
    Something not mentioned yet: `(type)value;` and `type(value);` are different - the latter defines a variable called `value`. – M.M Jul 22 '15 at 20:41

5 Answers5

52

There is no difference; per the standard (§5.2.3):

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).

Since the question specified the difference between type(value) and (type)value, there is absolutely no difference.

If and only if you're dealing with a comma-separated list of values can there be a difference. In this case:

If the expression list specifies more than a single value, the type shall be a class with a suitably declared constructor (8.5, 12.1), and the expression T(x1, x2, ...) is equivalent in effect to the declaration T t(x1, x2, ...); for some invented temporary variable t, with the result being the value of t as an rvalue.

As Troubadour pointed out, there are a certain names of types for which the type(value) version simply won't compile. For example:

char *a = (char *)string;

will compile, but:

char *a = char *(string);

will not. The same type with a different name (e.g., created with a typedef) can work though:

typedef char *char_ptr;

char *a = char_ptr(string);
Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
  • 8
    Um, no there is no difference. +1 – Johannes Schaub - litb Oct 30 '09 at 21:37
  • For the record, I didn't downwovte. – Troubadour Oct 30 '09 at 21:49
  • And *if* there is a comma separated list of expression, the first form cannot be used at all. It only works for single expressions of course. So in fact, in any circumstances, having a type named by `type`, then `type(value)` and `(type)value` is the same. – Johannes Schaub - litb Oct 30 '09 at 21:51
  • 1
    @litb: good point -- to try to include more than one expression, you'd have to do `(type)(expression-list)`, and at that point you'd just have redundant parens about the type... – Jerry Coffin Oct 30 '09 at 21:56
  • 2
    So nobody wants to answer my question about type being something like char*. The fact that it's a syntax error in the second case is okay is it? Oh, I guess that char* doesn't come under all types. You learn something new every day here. ;) – Troubadour Oct 30 '09 at 21:58
  • @Jerry, It's worse because you would cast the result of the comma operator. – Johannes Schaub - litb Oct 30 '09 at 22:00
  • 3
    @Troubadour, the question was about `type`, not about `type*`. It still works for all types, because making a suitable typedef will make `type` denote `char*`. – Johannes Schaub - litb Oct 30 '09 at 22:05
  • 2
    @litb: What a load of nonsense. char* is a perfectly good type. It could be used as T in template. I know you can use typedef to circumvent it, but then you would have known that if you'd read my earlier comment. – Troubadour Oct 30 '09 at 22:06
  • Well, I took it as a question about the difference in meaning between the two (when they compile). You're certainly right, however, that some "types" must be parenthesized for the code to compile at all. My apologies for the previous comment -- I missed your point entirely. – Jerry Coffin Oct 30 '09 at 22:06
  • 7
    @Troubadour, i see now what you mean. But what i said was that it works for all types, not just for "builtins" like you said. The point is that `char*` and `identity::type` (for a template like `boost::mpl::identity`) and `type` when `type` was typedefed to `char*` all denote the same type. The question is worded in such a way that `type` should denote a type (obviously). `type` grammatically is a simple-type-specifier, but `char*` is not. It's a `type-id`, which you can pass as template arguments. Also, please don't insult people by saying they write "nonsense". – Johannes Schaub - litb Oct 30 '09 at 22:13
  • 3
    So, it's wrong to say that "there are a certain types for which the type(value) version simply won't compile [but for which the other version will]", because it certainly compiles for the type denoted by `char*` - if you typedef it or if you write it as `identity::type(value)` – Johannes Schaub - litb Oct 30 '09 at 22:23
  • @litb: Yes, I've already accepted I was wrong about built-ins. Right away in fact. The question is about as short as it gets and is worded in no such way that you can draw any conclusions about type. – Troubadour Oct 30 '09 at 22:27
  • @litb: Oh, and please don't insult people by responding to comments that you've only half-read. – Troubadour Oct 30 '09 at 22:29
  • 2
    To show a better example: I tell you that `a * b` is equal to `b * a` and you say "No, that's not true for a = 1 and b = 2, because: `0+1 * 2` is 2 and `2 * 0+1` is 1." – Johannes Schaub - litb Oct 30 '09 at 22:36
13

There is no difference; the C++ standard (1998 and 2003 editions) is clear about this point. Try the following program, make sure you use a compiler that's compliant, such as the free preview at http://comeaucomputing.com/tryitout/.

#include <cstdlib>
#include <string>
int main() {
  int('A'); (int) 'A'; // obvious
  (std::string) "abc"; // not so obvious
  unsigned(a_var) = 3; // see note below
  (long const&) a_var; // const or refs, which T(v) can't do
  return EXIT_SUCCESS;
}

Note: unsigned(a_var) is different, but does show one way those exact tokens can mean something else. It is declaring a variable named a_var of type unsigned, and isn't a cast at all. (If you're familiar with pointers to functions or arrays, consider how you have to use a parens around p in a type like void (*pf)() or int (*pa)[42].)

(Warnings are produced since these statements don't use the value and in a real program that'd almost certainly be an error, but everything still works. I just didn't have the heart to change it after making everything line up.)

  • 2
    @squelart: Not at first, but once it got close I worked it in, and just didn't have the heart to change it in order to get rid of the warnings. –  Mar 06 '10 at 04:25
7

There is no difference when both are casts, but sometimes 'type(value)' is not a cast.

Here's an example from standard draft N3242, section 8.2.1:

struct S 
{
    S(int);
};

void foo(double a) 
{
    S w( int(a) ); // function declaration
    S y( (int)a ); // object declaration
}

In this case 'int(a)' is not a cast because 'a' is not a value, it is a parameter name surrounded by redundant parentheses. The document states

The ambiguity arising from the similarity between a function-style cast and a declaration mentioned in 6.8 can also occur in the context of a declaration. In that context, the choice is between a function declaration with a redundant set of parentheses around a parameter name and an object declaration with a function-style cast as the initializer. Just as for the ambiguities mentioned in 6.8, the resolution is to consider any construct that could possibly be a declaration a declaration.

dog44wgm
  • 535
  • 5
  • 17
1

In c there is no type (value), while in c/c++ both type (value) and (type) value are allowed.

macio.Jun
  • 9,647
  • 1
  • 45
  • 41
0

To illustrate your options in C++ (only one has a safety check)

#include<boost/numeric/conversion/cast.hpp> 

using std::cout;
using std::endl;
int main(){

    float smallf = 100.1;

    cout << (int)smallf << endl; // outputs 100 // c cast
    cout << int(smallf) << endl; // outputs 100 // c++ constructor = c cast

    cout << static_cast<int>(smallf) << endl; // outputs 100
//  cout << static_cast<int&>(smallf) << endl; // not allowed
    cout << reinterpret_cast<int&>(smallf) << endl; // outputs 1120416563
    cout << boost::numeric_cast<int>(smallf) << endl; // outputs 100

    float bigf = 1.23e12;

    cout << (int)bigf << endl; // outputs -2147483648
    cout << int(bigf) << endl; // outputs -2147483648

    cout << static_cast<int>(bigf) << endl; // outputs -2147483648
//  cout << static_cast<int&>(bigf) << endl; // not allowed
    cout << reinterpret_cast<int&>(bigf) << endl; // outputs 1401893083
    cout << boost::numeric_cast<int>(bigf) << endl; // throws bad numeric conversion
}
alfC
  • 14,261
  • 4
  • 67
  • 118