0

Let's assume I have a macro like this:

#define IS_SIGNED_B(T) (static_cast<T>(-1)<0)

Would it be alright to write it as

#define IS_SIGNED_B(T) (T(-1)<0)

Knowing that T is (should) always be a fundamental type. And various other cases where I need a certain value to be explicitly of a certain type.

I know this can cause issues for situations like:

signed char(0);

But knowing that I have fundamental types typedef'ed as:

typedef signed char Int8;
Int8(0);

Are there any other issues other than this? Can the constructor of a fundamental type be considered identical to a static cast?

EDIT: I know about the existence of std::numeric_limits and std::is_signed. This was just an example. Not the actual case. My apologies for not mentioning that.

SLC
  • 2,167
  • 2
  • 28
  • 46
  • 2
    Personally I would avoid all of this and us [`std::numeric_limits::is_signed`](http://en.cppreference.com/w/cpp/types/numeric_limits/is_signed) – NathanOliver Jan 19 '17 at 18:36
  • 2
    Why do you need a macro for this when [`std::is_signed`](http://en.cppreference.com/w/cpp/types/is_signed) exists? – Borgleader Jan 19 '17 at 18:36
  • You seem to be under the impression that `T(-1)` calls the constructor of `T` for `T == int`, however, that is just alternative syntax for a c-style cast which probably is implemented as a `static_cast`, so there should be no difference. – nwp Jan 19 '17 at 18:37
  • @Borgleader Nice. I didn't know they made a free version for . TIL :) – NathanOliver Jan 19 '17 at 18:37
  • 1
    I know about the existence of std::numeric_limits and/or type_traits. This was just an example. I couldn't think of a better one at the moment. – SLC Jan 19 '17 at 18:38
  • Well you must have real code where you think you need this. Can you share that? Generally though "proper practice" is to use `static_cast` since the C style cast does a lot of other things besides static casting. – NathanOliver Jan 19 '17 at 18:40
  • I guess I'll just use a static cast. My question was more like "*can the constructor of a fundamental type be considered identical to a static cast*" – SLC Jan 19 '17 at 18:42
  • Looks like [this](http://stackoverflow.com/questions/103512/in-c-why-use-static-castintx-instead-of-intx) might have your answer. Specifically [this](http://stackoverflow.com/a/103871/4342498) one. – NathanOliver Jan 19 '17 at 18:44
  • @NathanOliver I see. I encountered that question but mine was more limited to just fundamental types. So I though it would be a different case. – SLC Jan 19 '17 at 18:47

2 Answers2

4

Can the constructor of a fundamental type be considered identical to a static cast?

Fundamental types have no constructors. Int8(0) is an explicit type conversion and alternative syntax for (Int8)0. This is known as C-style cast. The alternative syntax is called a functional cast expression.

For fundamental integral types, C-style cast is equivalent to a static_cast. But it is not equivalent in general. If there is no static_cast available, then it will perform reinterpret_cast (or const_cast, or the combination of const_cast, and one of the other casts).

Are there any other issues other than this?

I don't quite understand what issue or issues you have presented, but yes explicit type conversions do have big issues. The major issue is that programmers aren't perfect, and you cannot assume that T is (should) always be a fundamental type always holds. You want the compiler to catch such mistakes. A C-style cast will hide some of the mistakes that you or your colleagues might make, and replace error messages with undefined behaviour.

Perhaps in the context of your macro, there is little danger of undefined behaviour, but as you said, that was just an example. A good rule of thumb: Prefer using the exact type of *_cast that you intend, instead of letting C-style cast pick one of the casts that might not be what you intended.

eerorika
  • 232,697
  • 12
  • 197
  • 326
  • Thank you. This all I wanted to know. Whether it can be considered a static cast or a C-style cast. It's been bugging me for a while and I guess I forgot the cases where I needed to have a clear understanding of this. My apologies for not asking the question properly. – SLC Jan 19 '17 at 19:01
2

The first thing you should do is stop using the C preprocessor needlessly.

Just use std::is_signed.

Failing that, something like:

template<class T>
constexpr
std::integral_constant<bool,
  (static_cast<T>(-1)<0)
> is_signed_b() { return {}; }

which is an industrial strength version of your macro.

It returns an empty class which has a constexpr conversion-to-bool whose value is the result you want. It can be evaluated at compile time (in fact, it is hard for it not to be).


But what is wrong with your macro?

First, your macro should read:

#define IS_SIGNED_B(...) (static_cast<__VA_ARGS__>(-1)<0)

because

IS_SIGNED_B( std::tuple_element_t<some_tuple, Is> )...

breaks needlessly with your implementation.

The C preprocessor doesn't understand C++. It doesn't understand that ,s are not contained in () or {} anymore.

As noted, if T is a two-word type, your code breaks.

Another issue is that if you feed it a type that isn't an integral type, strange things can happen. C-style casts are strong, and will work for pointer types. So you'll get the pointer-type value of -1 on the left hand side. On the right, you'll get 0, which implicitly converts to the null value of any pointer type. Then things are compared. This paragraph contains all kinds of undefined behavior.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524