为什么const_cast对std :: function的参数不起作用?

时间:2019-06-13 23:19:46

标签: c++ reinterpret-cast c++20 const-cast

我正在提供成员函数的const和非const变体,其中我重用const版本以按照Scott Meyers的书中的described in this answer来实现非const版本。

const版本采用以下类型的参数:

const std::function< void (const Foo &) > &

vs非常量采用类型为参数的

const std::function< void (      Foo &) > &

在实现中,我必须使用reinterpret_cast,因为const_cast无法正常工作。

例如:

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 );

这不是本着const_cast的精神吗?这仅仅是对语言定义的疏忽,也许可以在C ++ 2x中修复,还是const_cast永远不会符合这里的精神?

这是更完整的代码:

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 );
}

2 个答案:

答案 0 :(得分:4)

通常来说,使用const_cast来抛弃模板参数中出现的const并不安全。例如,考虑以下代码(当然,有些人为设计):

template <typename T> struct Wrapper {
    int x;
};

template <> struct Wrapper<char *> {
    double y;
};

在这里,指向Wrapper<const char *>的指针指向的对象与Wrapper<char *>完全不同,因此执行const_castWrapper<const char *> *变成{{1} }会导致指向包含Wrapper<char *> *的{​​{1}}的指针,现在指向包含struct的{​​{1}}的指针,这打破了目前我不知道的语言规则。 :-)

通常,以这种方式int是不安全的,因此语言规范不允许struct像这样使用,这就是您的情况,即使操作变得直观在某种意义上说,该语言不允许您使用double编写代码。

我相当确定,在此处使用const_cast会导致未定义的行为,因为当const_cast和{时,语言会认为const_castreinterpret_cast是不同的,不兼容的类型{1}}不同。碰巧,它可能恰好在您的系统上工作,但是我不相信您可以放心地认为这会起作用。

答案 1 :(得分:1)

reinterpret_cast是未定义的行为。 const_cast是不合适的,因为模板参数的const性质是您无法抛弃的。

解决这个问题的简单方法是,不要。摆脱一些参考。交换实施它的人。

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存储与呼叫兼容的内容。

如果您使用Foo&,则可以调用包含Foo const&的函数。因此,您可以将std::function<void(Foo const&)>存储在std::function<void(Foo&)>中。

std::function中包装内容可能涉及分配。因此,您可能希望找到高质量的function_view<Sig>来代替对std::function的使用。

在另一条评论中,您声明该代码处于关键循环中。完全消除std::function是一个好方法,或者至少减少类型擦除抽头并以某种方式将大量数据传递给它。

const / unconst的反转仍然合适;我们以可变的方式实现const而不是以const的方式实现const,因为一个是协变运算,另一个是协变运算。 See here