Question:
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 );
}
It works perfectly, except that it is a bug . 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 for std::find_if( )
:
template< class InputIt, class UnaryPredicate > InputIt find_if( InputIt first, InputIt last, UnaryPredicate p );
where 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 ZoneData
class is more complex than shown: it includes std::vector
and other little things , and its copy is not trivial (neither in time nor in memory usage).
- Is it possible to avoid creating
ZoneData
in lambda calls, for future occasions, without touching theZoneData
class?
Answer:
It may be a good idea to use a static_assert
that fails if the parameter of the lambda is not a reference, to achieve this we need to use the <type_traits>
header and its std::is_reference
utility:
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 a hitch:
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 );
} );