Приведение между указателем на член и указателем на константный член

Я пытался найти это, но каждый термин, о котором я думаю, заканчивается совершенно не связанными результатами.

У меня есть функция (шаблон), которая принимает указатель на член в качестве параметра, но я не могу неявно трактовать член, на который указывают, как const. Использование const_cast работает, но я бы хотел не вызывать его явно, если смогу.

struct MyStruct
{
  int *_array;
  int _size;
};

template<typename C, typename T>
void DoSomething(T* C::* arr, int siz)
{
  // do some read-only stuff with the member here
}

template<typename C, typename T>
void ConstDoSomething(T* C::* arr, int siz)
{
  DoSomething<C, T const>(arr, siz);
  // DoSomething<C, T const>(const_cast<T const* C::*>(arr), siz); // works
}

MyStruct ms;
ConstDoSomething<MyStruct const, int>(&MyStruct::_array, ms._size); // note:   cannot convert ‘arr’ (type ‘int* MyStruct::*’) to type ‘const int* MyStruct::*’

Это упрощенный пример, который демонстрирует проблему с более сложным деревом классов. Я пытаюсь избежать приведения, потому что это будет требоваться вызывающим кодом (например, человек, использующий шаблон (ы) класса).


ОБНОВЛЕНИЕ: Когда я впервые опубликовал это, я случайно использовал пример кода, который не генерирует ту же ошибку. Мне потребовалось немало тестов, чтобы определить основную причину, а именно то, что я добавил квалификатор const в аргументы шаблона. Приведенный выше пример теперь правильно демонстрирует поведение, которое я использую.

2 ответа

Какой компилятор вы используете? Я попробовал ваш код Visual Studio 2012 и 2013, и он был скомпилирован без каких-либо предупреждений или ошибок. В любом случае - вы должны попробовать const_cast вместо static_cast, когда вы играете с constness

Вместо лучшего решения (пока, по крайней мере) я создаю отдельную перегрузку для приема указателя на неконстантный член, а затем использую const_cast для вызова исходных методов.

Как уже отмечалось, приведенный выше пример был сделан для простоты и ясности, но на самом деле я использую несколько шаблонов классов, каждый из которых наследуется от другого, и так далее. Это привело к следующему довольно некрасивому решению:

// C = containing class (for pointer-to-members, std::nullptr_t if none)
// T = type of data being referenced

template<typename _C, typename _T> struct MakeMemberPointer // STL doesn't provide this??
{
public:
  typedef _T _C::* Type;
};

// Note: base-class template has specialization for C == std::nullptr_t
typedef typename std::conditional<std::is_same<C, decltype(nullptr)>::value,
  T const* C::*,
  T const*
>::type N;

typedef typename std::conditional<std::is_member_pointer<T>::value,
  typename MakeMemberPointer<
    C,
    typename std::add_pointer<
      typename std::remove_const<
        typename std::remove_reference<
          decltype(*(static_cast<C*>(nullptr)->*static_cast<N>(nullptr))) // T const&
        >::type // reference removed -> T const
      >::type // const removed -> T
    >::type // pointer added -> T*
  >::Type, // member pointer -> T* C::*
  typename std::add_pointer<typename std::remove_const<typename std::remove_pointer<N>::type>::type>::type
>::type NNoConst;

void DoSomething(N t) noexcept
{
}

void DoSomething(NNoConst t) noexcept
{
  DoSomething(const_cast<N>(t));
}

Класс, содержащий все это, является только константным классом, производным от неконстантного базового класса (но используется здесь с константными членами из-за аргументов шаблона). Объявление всего этого внутри класса является более предпочтительным (для меня), чем использование const_cast в вызывающем коде, но я все еще не понимаю, почему gcc не позволяет это преобразование неявным образом (в конце концов, это просто добавление квалификаторов const!!).

Другие вопросы по тегам