Почему const_cast не работает с аргументами std :: function? - PullRequest
3 голосов
/ 14 июня 2019

Я предоставляю константную и неконстантную вариацию функции-члена, где я повторно использую константную версию для реализации неконстантной версии как , описанное в этом ответе для книг Скотта Мейерса.

Константная версия принимает аргумент типа:

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

против неконстантных принимает аргумент типа:

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

В реализации я должен использовать reinterpret_cast, потому что const_cast не работает.

например,:.

const std::function< void (Foo &) > & Processor;
reinterpret_cast< const std::function< void (const Foo &) > & >( Processor );

против

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 ]

4 голосов
/ 14 июня 2019

Вообще говоря, небезопасно использовать const_cast для отбрасывания const ness, которое появляется в аргументах шаблона. Например, рассмотрим этот (правда, несколько надуманный) код:

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

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

Здесь указатель на Wrapper<const char *> указывает на совсем другой объект, чем Wrapper<char *>, поэтому выполнение const_cast для превращения Wrapper<const char *> * в Wrapper<char *> * приведет к указателю на struct содержащий int, теперь указывающий на struct, содержащий double, нарушая какое-то языковое правило, имя которого ускользает от меня на данный момент. : -)

Так как в общем случае const_cast таким образом небезопасно, языковая спецификация не позволяет использовать const_cast таким образом, поэтому в вашем случае, даже если операция имеет интуитивный смысл, язык запрещает ваш код с const_cast.

Я вполне уверен, что использование reinterpret_cast приводит к неопределенному поведению, поскольку язык рассматривает std::function<T1> и std::function<T2> как разные, несовместимые типы, когда T1 и T2 не совпадают. Может случиться, что ваша система будет работать по чистой случайности, но я не верю, что вы можете смело предполагать, что это сработает.

1 голос
/ 14 июня 2019

reinterpret_cast здесь неопределенное поведение. const_cast не подходит, потому что const ness параметров шаблона не то, что вы можете отбросить.

Самый простой способ решить это, ну, не надо. Избавьтесь от некоторых ссылок. Поменяйтесь, кто его реализует.

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 / unfst все еще уместна; мы реализуем const в терминах изменяемого, а не изменяемого в терминах const, потому что одна - ковариантная операция, другая - контравариантная операция. Смотрите здесь .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...