Ошибка использования unique_ptr с указателем на функцию-член

У меня есть класс, как показано ниже

class A
{
public:
A(int key)          : m_key(key) {}
int Key() const     {return m_key;}

private:
int m_key;
};

Я тестирую с использованием unique_ptr с указателем на функцию-член

int (A::*MemFun)() const;
MemFun = &A::Key;
( std::unique_ptr<A>(new A(10))       ->*MemFun ) (); // Error C2296
( std::unique_ptr<A>(new A(10)).get() ->*MemFun ) (); // okay
(*std::unique_ptr<A>(new A(10))        .*MemFun ) (); // okay

Первая выдает ошибку компиляции (VC2010 выдает ошибку C2296, недопустимо, левый оператор включает std :: unique_ptr<_Ty>). Зачем? Благодарю.

5

Решение

Кажется operator->*() оператор не перегружен для std::unique_ptr<T>, Причина Зачем этот оператор не определен, не совсем ясно, хотя я думаю, что в то время, когда были предложены умные указатели, необходимая механика для борьбы с подходящими перегрузками отсутствовала.

Проблема в том, что operator->*() нужно иметь дело с возвращением связанного результата. Для простой функции-члена это достаточно просто, но для функций это не совсем тривиально. Вот минималистичный вариант unique_ptr<T> Шаблон класса, который просто показывает реализацию, будет выглядеть так:

template <typename T>
struct unique_ptr
{
T* p_;
unique_ptr(T* p): p_(p) {}
T* operator->() { return this->p_; }
template <typename R>
R& operator->*(R T::*mem) { return this->p_->*mem; }
template <typename R>
auto operator->*(R (T::*mem)()) ->decltype(std::bind(mem, this->p_))
{
return std::bind(mem, this->p_);
}
};

Эта версия просто справляется с указателями на переменные-члены и указателем на функции-члены без аргументов. Мне нужно немного поразмышлять над версией operator->*() оператор для произвольного числа аргументов. Версия для указателя на переменные-члены тривиальна: просто нужно вернуть ссылку на соответствующий член. Версия для функций-членов должна создать вызываемый объект с первым (неявным) параметром, связанным с правильным объектом.

Чтобы разобраться с произвольным числом аргументов, нужно поиграть с переменными аргументами. Определение unique_ptr<T> также работа с указателями на функции-члены, принимающими аргументы, может выглядеть примерно так:

template <typename T>
struct unique_ptr
{
private:
T* p_;
template <typename R, typename... A, int... I>
auto bind_members(R (T::*mem)(A...), indices<I...>)
-> decltype(std::bind(mem, this->p_, placeholder<I + 1>()...))
{
return std::bind(mem, this->p_, placeholder<I + 1>()...);
}

public:
unique_ptr(T* p): p_(p) {}
T* operator->() const { return this->p_; }
template <typename R>
R& operator->*(R T::*mem) { return this->p_->*mem; }
template <typename R>
auto operator->*(R (T::*mem)()) ->decltype(std::bind(mem, this->p_))
{
return std::bind(mem, this->p_);
}
template <typename R, typename... A>
auto operator->*(R (T::*mem)(A...))
-> decltype(this->bind_members(mem,
typename indices<sizeof...(A) - 1>::type())) {
return this->bind_members(mem,
typename indices<sizeof...(A) - 1>::type());
}
};

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

template <int... Indices> struct indices;
template <> struct indices<-1> { typedef indices<> type; };
template <int... Indices>
struct indices<0, Indices...>
{
typedef indices<0, Indices...> type;
};
template <int Index, int... Indices>
struct indices<Index, Indices...>
{
typedef typename indices<Index - 1, Index, Indices...>::type type;
};

template <int I>
struct placeholder
: std::integral_constant<int, I>
{
};

namespace std
{
template <int I>
struct is_placeholder<placeholder<I>>
: std::integral_constant<bool, true>
{
};
}
5

Другие решения

->* синтаксис — это один оператор (один из операторов «указатель на член»). Этот оператор может быть перегружен, но std::unique_ptr не делает этого

2

По вопросам рекламы [email protected]