7

The following compiles in Visual Studio but fails to compile under g++.

int main()
{
    int a = unsigned char('0');
    return 0;
}

Is unsigned char() a valid way to cast in C++?

qaz
  • 167
  • 2
  • 6

8 Answers8

11

No, it's not legal.

A function-style explicit type conversion requires a simple-type-specifier, followed by a parenthesized expression-list. (§5.2.3) unsigned char is not a simple-type-specifier; this is related to a question brought up by James.

Obviously, if unsigned char was a simple-type-specifier, it would be legal. A work-around is to use std::identity:

template <typename T>
struct identity
{
    typedef T type;
};

And then:

int a = std::identity<unsigned char>::type('0');

std::identity<unsigned char>::type is a simple-type-specifier, and its type is simply the type of the template parameter.

Of course, you get a two-for-one with static_cast. This is the preferred casting method anyway.

Community
  • 1
  • 1
GManNickG
  • 494,350
  • 52
  • 494
  • 543
  • 1
    I honestly don't see how the question you quote is about casting. Or your answer, for that matter. –  Jul 08 '10 at 23:31
  • @Neil: The question is about type specifiers, and `unsigned char` isn't valid. `std::identity::type` is, and the code becomes legal. – GManNickG Jul 08 '10 at 23:46
  • @GMan The question is about casting, not type specifiers. unsigned int is a valid type, it is not a valid C++ function-style cast operator. –  Jul 08 '10 at 23:49
  • 1
    @Neil: He wants to know why the cast doesn't work, and this is because of type-specifiers. It's completely false to say the question has nothing to do with type-specifiers, they're the exact reason the code isn't legal. `unsigned int` is indeed a valid type, but that's not all thats required for `T(x)` to work; indeed `unsigned char(0)` is not legal. Why this is so is exactly dependent on the requirements type specifiers must satisfy, namely it must be a simple-type-specifier, which `unsigned char` is not. `std::identity::type` is. – GManNickG Jul 08 '10 at 23:55
  • @GMan And it is not a simple type because it is prefixed by a space-separated qualifier. And you certainly don't need to use things like identity to perform the cast. –  Jul 09 '10 at 00:00
  • 2
    @Neil: To perform it in a function-style cast you do, which is what the question is about. He wants to know why a function-style cast with `unsigned char` isn't working; telling him to do something else doesn't answer his question. Also, it doesn't really have anything to do with spaces; the `identity` solution has spaces. – GManNickG Jul 09 '10 at 00:02
  • 1
    @GMan Well if he is hell bent on using a function-style cast, maybe, but I would have thought static_cast was better practice, or even in this case (and I don't know of any reason for casting a char to an unsigned int and then storing in it an int - not to say there isn't one) a C-style cast. –  Jul 09 '10 at 00:07
  • 1
    @Neil: Oh sure, I think actually using `identity` would be terrible practice, but it's a technical question, not a best-practices question. I answer why the function-cast isn't working, how to get a minimal work-around, and recommend a best-practice. I think the first two are the answers to his questions, and the third is to save him from doing ugly coding. But I think ignoring those first two points is a failure to answer his question. – GManNickG Jul 09 '10 at 00:09
  • @GMan But your work around is not a function-style cast, and it is certainly not "minimal" - static_cast is 9 characters shorter, and a C-style cast shorter still. –  Jul 09 '10 at 00:17
  • @Neil: How is it not a function-style cast? It's a `simple-type-specifier` followed by a parenthesized `expression-list`, the exact definition of such a cast. It's minimal *for a function-style cast* in that all you need is an `id/identity` meta-function to generate a `simple-type-specifier`. Comparing it to other casts is missing the point. – GManNickG Jul 09 '10 at 00:22
  • @GMan A function style cast is a basic language construct - the identity struct as you present it it is just that, a struct, not part of the language. –  Jul 09 '10 at 00:26
  • 3
    I don't understand the objection to this answer. The question seems to be very specifically about function-style cast. The answer correctly and concisely explains why this particular cast as written is non-standard, with references to Standard terms, and also explains how to make it standard while retaining the structure of the code. – Pavel Minaev Jul 09 '10 at 00:27
  • 1
    @Neil: `struct`'s aren't part of the language? You should read §5.2.3 and tell me what part of that is violated by the `identity` solution. – GManNickG Jul 09 '10 at 00:52
  • @GMan Oh come on. Of course the struct keyword is part of the language - a specific struct type of course isn't, though it may be part of the standard library. The library != the language. You are using terms like simple-type-specifier which describe the grammar of the language, not the features of the library. –  Jul 09 '10 at 00:55
  • @Neil: The point is that function-cast doesn't somehow require the type be primitive. I'm failing to see how it's not a function-style cast, seeing that it fits all the requirements to be one. – GManNickG Jul 09 '10 at 00:59
  • By the way does this mean 'new unsigned char[5]' is also illegal? – qaz Jul 10 '10 at 12:00
  • 1
    @qaz: A valid question, but nope. The grammar says a new expression is `new` followed a `type-id`, like a variable declaration's type. The function-style cast is more strict, though, because of parsing issues(?). – GManNickG Jul 10 '10 at 16:50
  • 1
    Sadly, I don't think we can call it `std::identity` anymore, as it was removed from the draft standard library spec in the FCD (it was present as late as N3035, if I recall correctly). :-( Since it needs a new non- `std` home, maybe we should put it in the `litb` namespace and call it `litb::identity`, since he's one of its biggest proponents :-P – James McNellis Jul 10 '10 at 18:05
  • @James: wtfwhy :( Also, we should add `namespace litb { template using id = T;}` too. :) (So elegant!) – GManNickG Jul 10 '10 at 22:10
  • 1
    @GMan: Apparently it's because SGI's STL already had a `std::identity` that had a different purpose and function (cf. http://groups.google.com/group/comp.std.c++/browse_thread/thread/ac6fb36f6d35dd32/). – James McNellis Jul 10 '10 at 22:20
5

The prefered method of casting in C++ is to use static_cast like so:

int a = static_cast<unsigned char>( '0' );
wheaties
  • 35,646
  • 15
  • 94
  • 131
  • But this doesn't tell him whether his code is legal or not, and why. – GManNickG Jul 09 '10 at 00:10
  • You're right and I'm glad he accepted your answer as it contains a more indepth discussion. – wheaties Jul 09 '10 at 00:23
  • This thing also won't work with multi-arg constructors. Imagine he tries `string const(10, 'X')` and applies your suggestion to do `static_cast(10, 'X')` - it won't work, because it's the comma operator and tries to convert only a single char. However, @GMan 's solution still works. – Johannes Schaub - litb Jul 09 '10 at 08:21
  • Wait, what? We're talking about casting, not constructors. If you want to invoke an object's constructor you're not going to use `static_cast`. That's not it's purpose. It's purpose is to convert from one type to another legally allowed type. – wheaties Jul 09 '10 at 11:39
3

Try to add brackets int a = (unsigned char)('0');

or

typedef unsigned char uchar;

//inside main
int a = uchar('0');
mip
  • 8,355
  • 6
  • 53
  • 72
2

No, it isn't - a function-style cast cannot have a space in its name.

A case for a C-style cast perhaps:

int main() {
    unsigned char c = (unsigned char) '0' ;
}
1

I'm pretty sure it's a Microsoft extension.

fredoverflow
  • 256,549
  • 94
  • 388
  • 662
  • You think "unsigned char" is a Microsoft extension?! – wheaties Jul 09 '10 at 01:41
  • @wheaties: No, the casting syntax `type_identifier(expression)` is standard C++, whereas the casting syntax `type identifier(expression)` is a Microsoft extension. Note the space: `unsigned char` has a space. – fredoverflow Jul 09 '10 at 08:38
0

No, it isn't. But why the cast in the first place? This is perfectly valid,

int a = '0';
John
  • 289
  • 1
  • 2
0

Why are you even trying to cast from char to unsigned char and assigning that to an int? You're putting an unsigned value into a signed int (which is legal, but uncool).

Writing '0' gives you a char with value 48. You can try

int i = (int) '0';

That way, you take the char, cast it to an int, and use it. You could even say

int i = '0';

And that would do the same thing. What exactly are you trying to do?

EboMike
  • 76,846
  • 14
  • 164
  • 167
  • But `int i = (unsigned char)'\xFF';` wouldn't give the same results if the cast were removed. There's nothing "uncool" about preventing sign-extension during a widening conversion. – Ben Voigt Jul 09 '10 at 00:13
  • Well, the uncool part was referring to assigning an unsigned value to a signed variable. Although it's legit this way around, any sign mismatches are often oversights and can result in hidden bugs. – EboMike Jul 09 '10 at 03:38
0

Are you trying to get the integer 0 or the character '0' into it? The character '0' on most implementations namely is just the integer 48 but put into 8 bits.

The only difference between a char and an int is that char must be smaller or equal to short int. and int must be larger or equal than short int accordingly, this usually makes char 8 bits, short in 16, and in 32 nowadays.

Stuf like 'a'+2 to get 'c' works namely. If you have an array that is long enough, you can also index it like array[' '] to get index 32.

If you're trying to cast it to the integer value 0, that would require an actual function that determines that.

Zorf
  • 6,334
  • 2
  • 29
  • 24
  • 1
    `char` has one important difference from `int`: `int` is a signed type; `char` is not. You might or might not be able to store negative values in a `char`. – Dennis Zickefoose Jul 09 '10 at 01:09
  • 1
    @Dennis Zickefoose, Depends on the implementation, just as in int. Implementations are free to choose if they without a qualifier make it signed or unsigned. – Zorf Jul 09 '10 at 05:43
  • 1
    No, `int` is a signed type by default. This is manditory. `char` is the only type distinct from its signed counterpart. This distinction is why a cast like in the original question is actually useful from time to time. – Dennis Zickefoose Jul 09 '10 at 14:57
  • @Dennis Zickerfoose Really? I thought that officially implementations were free to choose this themselves but de facto all choose char as unsigned and int as signed? – Zorf Jul 09 '10 at 22:26