Reputation: 43129
I'm providing a const and non-const variation of a member function where I reuse the const version to implement the non-const version as described in this answer per Scott Meyers books.
The const version takes an argument of type:
const std::function< void (const Foo &) > &
vs the non-const takes an argument of type:
const std::function< void ( Foo &) > &
In the implementation, I have to use reinterpret_cast
because const_cast
is not working.
E.g.,:
const std::function< void (Foo &) > & Processor;
reinterpret_cast< const std::function< void (const Foo &) > & >( Processor );
vs
const std::function< void (Foo &) > & Processor;
const_cast< const std::function< void (const Foo &) > & >( Processor );
Wouldn't this be in the spirit of what const_cast
is meant for? Is this just an oversight in the language definition to perhaps be fixed in C++2x or would const_cast
never be in the spirit of things here?
Here is more complete code:
void Collection::ProcessCollection(const std::function< void (const Foo &) > & Processor) const
{
for( int idx = -1 ; ++idx < m_LocalLimit ; )
{
if ( m_Data[ idx ] )
{
Processor( m_Data[idx] );
}
}
const int overflowSize = OverflowSize();
for( int idx = -1 ; ++idx < overflowSize ; )
{
Processor( (*m_Overflow)[ idx ] );
}
}
void Collection::ProcessCollection(const std::function< void (Foo &) > & Processor)
{
const Collection * constThis = const_cast< const Collection * >( this );
const std::function< void (const Foo &) > & constProcessor
= reinterpret_cast< const std::function< void (const Foo &) > & >( Processor );
constThis->ProcessCollection( constProcessor );
}
Upvotes: 3
Views: 696
Reputation: 275385
reinterpret_cast
here is undefined behavior. const_cast
isn't appropriate, because the const
ness of template parameters isn't something you can cast away.
The easy way to solve this is, well, don't. Get rid of some references. Swap who implements it.
void Collection::ProcessCollection(std::function< void (const Foo &) > Processor) const
{
Collection * mutableThis = const_cast< Collection * >( this );
mutableThis->ProcessCollection( Processor );
}
void Collection::ProcessCollection(std::function< void (Foo &) > Processor)
{
for( int idx = -1 ; ++idx < m_LocalLimit ; )
{
if ( m_Data[ idx ] )
{
Processor( m_Data[idx] );
}
}
const int overflowSize = OverflowSize();
for( int idx = -1 ; ++idx < overflowSize ; )
{
Processor( (*m_Overflow)[ idx ] );
}
}
std::function
stores things that are call-compatible with it.
If you take a Foo&
, you can call a function expecting a Foo const&
with it. So you can store a std::function<void(Foo const&)>
within a std::function<void(Foo&)>
.
Wrapping something in a std::function
can involve allocating. So you may want to find a high-quality function_view<Sig>
to replace your use of std::function
.
In another comment, you state that this code is in a critical loop. Eliminating std::function
entirely is a good move, or at least reducing the type erasure taps and passing batches of data to it somehow.
The inversion of const/unconst is still appropriate; we implement const in terms of mutable instead of mutable in terms of const because one is a covariant operation, the other is a contravariant operation. See here.
Upvotes: 1
Reputation: 372784
Generally speaking, it's not safe to use const_cast
to cast away const
ness that appears in template arguments. For example, consider this (admittedly, somewhat contrived) code:
template <typename T> struct Wrapper {
int x;
};
template <> struct Wrapper<char *> {
double y;
};
Here, a pointer to a Wrapper<const char *>
points to a very different object than a Wrapper<char *>
, so doing a const_cast
to turn a Wrapper<const char *> *
into a Wrapper<char *> *
would result in a pointer to a struct
containing an int
now pointing at a struct
containing a double
, breaking some language rule whose name eludes me at the moment. :-)
Since in general it's not safe to const_cast
this way, the language spec doesn't allow for const_cast
to be used like this, which is why in your case, even though the operation makes intuitive sense, the language disallows your code with const_cast
.
I am fairly certain that using a reinterpret_cast
here leads to undefined behavior, since the language considers std::function<T1>
and std::function<T2>
to be different, incompatible types when T1
and T2
aren't the same. It may happen to work on your system by pure coincidence, but I don't believe you can safely assume this will work.
Upvotes: 4