Avoid temporary copies in call to lambdas

5

I have the following class:

class Utf8Writer {
  struct ZoneData {
    uint32_t begin; // codepoint inicial.
    uint32_t end; // codepoint final.
  };

  const ZoneData *findCodepoint( uint32_t cp, FontId fid );
};

const ZoneData *Utf8Writer::findCodepoint( uint32_t cp, FontId fid ) {
  auto iter = std::find_if( fid->cbegin( ), fid->cend( ), [cp] ( ZoneData i ) { return ( cp >= i.begin ) && ( cp <= i.end ); } );

  return ( iter == fid->cend( ) ) ? nullptr : &( *iter );
}

Works perfectly, except that is an error . What I really wanted to do is:

const ZoneData *Utf8Writer::findCodepoint( uint32_t cp, FontId fid ) {
  auto iter = std::find_if( fid->cbegin( ), fid->cend( ), [cp] ( const ZoneData &i ) { return ( cp >= i.begin ) && ( cp <= i.end ); } );

  return ( iter == fid->cend( ) ) ? nullptr : &( *iter );
}

In fact, if we look at the doc of std::find_if( ) :

template< class InputIt, class UnaryPredicate > InputIt find_if( InputIt first, InputIt last, UnaryPredicate p );

being UnaryPredicate - > bool pred( const Type &a );

I understand that, in the first version, the compiler creates a temporary one, calling the constructor-copy by default . It turns out that the class ZoneData is more complex than shown: it includes std::vector and other cosillas , and its copy is not trivial (neither in time nor in memory usage).

  • Is it possible to avoid creating temporary calls in lambda , for future occasions, without touching the class ZoneData ?
asked by Trauma 14.05.2018 в 11:15
source

1 answer

6

It may be a good idea to use a static_assert that fails if the lambda parameter is not a reference, to get it we need to use the header <type_traits> and its usefulness std::is_reference :

This code will fail:

const ZoneData *Utf8Writer::findCodepoint( uint32_t cp, FontId fid ) {
  auto iter = std::find_if( fid->cbegin( ), fid->cend( ), [cp] ( ZoneData i )
//          'i' es una instancia, podría ser copia o movido ---> ~~~~~~~~~~
{
    // La aserción falla pues 'i' no es una referencia
    static_assert(std::is_reference_v<decltype(i)>, "Evita copiar el parametro!");
    return ( cp >= i.begin ) && ( cp <= i.end );
} );

While this should work without problems:

const ZoneData *Utf8Writer::findCodepoint( uint32_t cp, FontId fid ) {
  auto iter = std::find_if( fid->cbegin( ), fid->cend( ), [cp] ( const ZoneData &i )
//                     'i' es una referencia, no será copia ---> ~~~~~~~~~~~~~~~~~
{
    // La aserción NO falla pues 'i' no es una referencia
    static_assert(std::is_reference_v<decltype(i)>, "Evita copiar el parametro!");
    return ( cp >= i.begin ) && ( cp <= i.end );
} );
    
answered by 14.05.2018 / 11:39
source