Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is an entirely new class compiled for each differently sized std::array?

As far as I understand, c++ templating works by compiling a separate class or function for every type needed. This seems logical for classes or functions that will only be called for a handful of different types / parameters, but for std::array it seems like this could result in the same class being compiled into hundreds of different versions.

I understand the advantages of std::array over C style arrays, but it seems like using the former would result in huge binary sizes compared to the latter if my above assumption is correct.

For example, if say in a large program we end up using 99 arrays of different sizes throughout the entire code, so that we effectively have:

int arr[1]   = { ... }
int arr[2]   = { ... }
int arr[...] = { ... }
int arr[99]  = { ... }
int arr[100] = { ... }

Or

 std::array<int, 1> arr   = { ... }
 std::array<int, 2> arr   = { ... }
 std::array<int, ...> arr = { ... }
 std::array<int, 99>  arr = { ... }
 std::array<int, 100> arr = { ... }

Would the std::array example end up with the entire class and all its functions being compiled into the binary 99 times?

like image 509
JShorthouse Avatar asked Aug 27 '19 14:08

JShorthouse


4 Answers

Yes, a new class is generated by the class template for each different set of template parameters.

But that class need not as-if exist in the runtime binary.

Most of the methods are short, and should be inlined at point of use. So they won't be emitted into the binary.

If you started taking the address of the methods and storing them, you'd start running into bloat, as you are forcing each distinct method to exist.

For example of a binary bloat generator:

template<std::size_t...Ns>
std::function<std::type_info const&()> stupid(std::size_t i, std::index_sequence<Ns...>) {
  std::function<std::type_info const&()> retval;
  (
    ((i || (retval = []()->std::type_info const&{
       return typeid( std::array<int, Ns> );
    })) && i--) && ...
  );
  return retval;
}
std::function<std::type_info const&()> stupid( std::size_t i ) {
  return stupid( i, std::make_index_sequence<100>{} );
}

this requires that the library contain rtti information for 100 different std::arrays.

But if you don't do that kind of thing, the rtti isn't needed. So it isn't injected into your binary.

And I can do the exact same thing with 100 different arrays.

template<std::size_t...Ns>
std::function<std::type_info const&()> stupid(std::size_t i, std::index_sequence<Ns...>) {
  std::function<std::type_info const&()> retval;
  (
    ((i || (retval = []()->std::type_info const&{
       return typeid( int[Ns] );
    })) && i--) && ...
  );
  return retval;
}
std::function<std::type_info const&()> stupid( std::size_t i ) {
  return stupid( i, std::make_index_sequence<100>{} );
}

a "class" in C++ isn't a heavy thing like in other OO languages. There is no global class state unless you force it to exist.

like image 162
Yakk - Adam Nevraumont Avatar answered Oct 07 '22 08:10

Yakk - Adam Nevraumont


Yes, std::array<int,1> will be compiled to a different class than std::array<int,2>.

But don't worry. since std::array is just a thin wrapper around c- arrays (int arr[2]), most of the methods will be inlined anyway.

so in a sense, std::array<int,1>::operator[] and std::array<int,2>::operator[] will be compiled into two different methods, but those 2 methods will be compiled to the same cpu-instructions, and will be inlined into caller function, when optimizations are turned on.

like image 24
David Haim Avatar answered Oct 07 '22 09:10

David Haim


Would the std::array example end up with the entire class and all its functions being compiled into the binary 99 times?

No, you have indeed one class instantiation for each different parameters...

But that won't include all methods. Only instantiated method would be generated.

In your case, you just use aggregate initialisation, so it is identical.

like image 43
Jarod42 Avatar answered Oct 07 '22 10:10

Jarod42


Yes, each class specialization will be instantiated for different template arguments.

An instantiation of a class specialization does not automatically instantiate its member function definitions. Only their declarations are instantiated. This does not influence on the binary code until the functions are used.

like image 23
Vlad from Moscow Avatar answered Oct 07 '22 09:10

Vlad from Moscow