Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Construct a 'long long'

Tags:

gcc

How do you construct a long long in gcc, similar to constructing an int via int()

The following fails in gcc (4.6.3 20120306) (but passes on MSVC for example).

myFunctionCall(someValue, long long());

with error expected primary-expression before 'long' (the column position indicates the first long is the location).

A simple change

myFunctionCall(someValue, (long long)int());

works fine - that is construct an int and cast to long long - indicating that gcc doesn't like the long long ctor.

Summary Solution

To summarize the brilliant explanation below from @birryree:

  • many compilers don't support long long() and it may not be standards compliant
  • constructing long long is equivalent to the literal 0LL, so use myFunctionCall(someValue, 0LL)
  • alternatively use a typedef long_long_t long long then long_long_t()
  • lastly, consider using uint64_t if you are after a type that is exactly 64 bits on any platform, rather than a type that is at least 64 bits, but may vary on different platforms.
like image 998
Zero Avatar asked May 10 '12 04:05

Zero


1 Answers

I wanted a definitive answer on what the expected behavior was, so I posted a question on comp.lang.c++.moderated and got some great answers in return. So a thank you goes out to Johannes Schaub, Alf P. Steinbach (both from SO), and Francis Glassborrow for some information

This is not a bug in GCC - in fact it will break across multiple compilers - GCC 4.6, GCC 4.7, and Clang complain about similar errors like primary expression expected before '(' if you try this syntax:

long long x = long long();

Some primitives have spaces, and that is not allowed if you want to use the constructor-style initialization because of binding (long() is bound, but long long() has a free long). Types with spaces in them (like long long) can not use the type()-construction form.

MSVC is more permissive here, though technically non-standard compliant (and it's not a language extension that you can disable).

Solutions/Workarounds

There are alternatives for what you want to do:

  • Use 0LL as your value in place of attempting long long() - they would produce the same value.

    This is how most code will be written too, so it will be most understandable to anyone else reading your code.

  • From your comments it seems like you really want long long, so you can typedef yourself to always guarantee you have a long long type, like this:

    int main() {
        typedef long long MyLongLong;
        long long x = MyLongLong(); // or MyLongLong x = MyLongLong();
    }
    
  • Use a template to get around needing explicit naming:

    template<typename TypeT>
    struct Type { typedef TypeT T(); };
    
    // call it like this:
    long long ll = Type<long long>::T();
    
  • As I mentioned in my comments, you can use an aliased type, like int64_t (from <cstdint>), which across common platforms is a typedef long long int64_t. This is a more platform dependent than the previous items in this list.

    int64_t is a fixed-width type that is 64-bits, which is typically how wide long long is on platforms like linux-x86 and windows-x86. long long is at least 64-bit wide, but can be longer. If your code will only run on certain platforms, or if you really need a fixed-width type, this might be a viable choice.

C++11 Solutions

Thanks to the C++ newsgroup, I learned some additional ways of doing what you want to do, but unfortunately they're only in the realm of C++11 (and MSVC10 doesn't support either, and only very new compilers either way would):

  • The {} way:

    long long ll{}; // does the zero initialization
    
  • Using what Johannes refers to as the 'bord tools' in C++11 with std::common_type<T>

    #include <type_traits>
    
    int main() {
        long long ll = std::common_type<long long>::type();
    }
    

So is there a real difference between () and initializing with 0 for POD types?

You say this in a comment:

I don't think default ctor returns zero always - more typical behaviour is to leave memory untouched.

Well, for primitive types, that is not true at all.

From Section 8.5 of the ISO C++ Standard/2003 (don't have 2011, sorry, but this information didn't change too much):

To default-initialize an object of type T means:

— if T is a non-POD class type (clause 9), the default constructor for T is called (and the initialization is ill-formed if T has no accessible default constructor);

— if T is an array type, each element is default-initialized;

otherwise, the object is zero-initialized.

The last clause is most important here because long long, unsigned long, int, float, etc. are all scalar/POD types, and so calling things like this:

int x = int();

Is exactly the same as doing this:

int x = 0;

Generated code example

Here is a more concrete example of what actually happens in code:

#include <iostream>

template<typename T>
void create_and_print() {
   T y = T();
   std::cout << y << std::endl;
}

int main() {
   create_and_print<unsigned long long>();

   typedef long long mll;
   long long y = mll();
   long long z = 0LL;

   int mi = int();
}

Compile this with:

g++ -fdump-tree-original construction.cxx

And I get this in the generated tree dump:

;; Function int main() (null)
;; enabled by -tree-original

{
  typedef mll mll;
  long long int y = 0;
  long long int z = 0;
  int mi = 0;

  <<cleanup_point <<< Unknown tree: expr_stmt
  create_and_print<long long unsigned int> () >>>>>;
  <<cleanup_point   long long int y = 0;>>;
  <<cleanup_point   long long int z = 0;>>;
  <<cleanup_point   int mi = 0;>>;
}
return <retval> = 0;



;; Function void create_and_print() [with T = long long unsigned int] (null)
;; enabled by -tree-original

{
  long long unsigned int y = 0;

  <<cleanup_point   long long unsigned int y = 0;>>;
  <<cleanup_point <<< Unknown tree: expr_stmt
  (void) std::basic_ostream<char>::operator<< ((struct __ostream_type *) std::basic_ostream<char>::operator<< (&cout, y), endl) >>>>>;
}

Generated Code Implications

So from the code tree generated above, notice that all my variables are just being initialized with 0, even if I use constructor-style default initialization, like with int mi = int(). GCC will generate code that just does int mi = 0.

My template function that just attempts to do default construction of some passed in typename T, where T = unsigned long long, also produced just a 0-initialization code.


Conclusion

So in conclusion, if you want to default construct primitive types/PODs, it's like using 0.

like image 162
逆さま Avatar answered Sep 22 '22 21:09

逆さま