c++ - optimization for ODR-used empty classes -


many of nowaday c++ code tend template-loaded in greatest extent. libraries: stl, boost.spirit, boost.mpl, etc among many other. encourages users declare functional objects in form struct s { /* presence of non-virtual member functions , operators, absense of non-static data members or non-empty base classes */ }; s const s{};. of them stateless (i.e. static_assert(std::is_empty< s >{}); holds). of them, odr-used, regardless of theirs emptyness data section of file growth 1 byte (sizeof(s) == 1 empty type s because addresses consequentially allocated objects should different). in simple grammars of boost.spirit there plenty of such odr-used empty classes. absolutely of no sense keep space them.

i tried test clang on coliru using following code (-ofast):

#include <utility> #include <type_traits>  #include <cstdlib> #include <cassert>  template< std::size_t index > struct s {};  namespace {  template< std::size_t index > s< index > value = {};  }  template< typename lhs, typename rhs > std::ptrdiff_t diff(lhs & l, rhs & r) {     return (static_cast< char * >(static_cast< void * >(&r)) - static_cast< char * >(static_cast< void * >(&l))); }  template< std::size_t base, std::size_t ...indices > std::ptrdiff_t bss_check(std::index_sequence< indices... >) {     return (diff(value< (base + indices) >, value< (base + indices + 1) >) + ...);  }  template< std::size_t size, std::size_t base > bool enumerate() {     return (bss_check< base >(std::make_index_sequence< size >{}) + 1 == size); }  template< std::size_t size, std::size_t ...bases > bool expand(std::index_sequence< bases... >) {     return (enumerate< size, (bases * size) >() && ...); }  template< std::size_t size = 100, std::size_t count = size > bool check() {     return expand< size >(std::make_index_sequence< count >{}); }  int main() {     static_assert(std::is_empty< s< 0 > >{});     assert((check< dim >()));     return exit_success; } 

and result (output of size utility dim == 100, i.e. 100 * 100 classes):

   text    data     bss     dec     hex filename  112724   10612       4  123340   1e1cc ./a.out 

if change signature of diff(lhs & l, rhs & r) diff(lhs l, rhs r) in order suppress odr-using, result is:

  text     data     bss     dec     hex filename   69140     608       8   69756   1107c ./a.out 

is equal (data section of interest) case of simple commenting of assert((check< dim >())); line (major part of text section predictable dce-optimized out):

   text    data     bss     dec     hex filename    1451     600       8    2059     80b ./a.out 

hence conclude there no optimization odr-used empty classes.

for explicitly specified template parameters there possibility use simple typefilter:

template< typename type > using ref_or_value = std::conditional_t< std::is_empty< std::decay_t< type > >{}, std::decay_t< type >, type && >; 

but there no simple workaround deduced template types @ mind.

is there implied above optimization in modern compilers? if yes, how enable it? if no, there technique achieve desired behaviour @ moment?

i know somtimes addresses of objects mutters much, not case in described above situation.

i think attribute variable or type (e.g. [[immaterial]]) handy. maybe such attribute (used classes) should deny possibility address of instances of attributed classes (compile-time hard error) or address-of operator & shoud return non-sense value (implementation-defined).

c++17 adding inline variables address of these issues explained in n4424. explains workarounds. global function objects can define them this:

// sum function object struct sum_f {     template<class t, class u>     auto operator()(t x, u y) const     {         return x+y;     }   };  template<class t> struct static_const_storage {     static constexpr t value = t(); };  template<class t> constexpr t static_const_storage<t>::value;   template<class t> constexpr const t& static_const() {     return static_const_storage<t>::value; }  static constexpr auto& sum = static_const<sum_f>(); 

this makes sum function object unique across translation units thereby avoiding bloat , odr violations. however, workaround doesn't work template variables, , best avoid them(if concerned executable bloat) until inline variables in c++17.


Comments