Пытаясь ответить на один вопрос здесь, я нашел этот вопрос:
Как рекурсивно разыменовать указатель (C ++ 03)?
Адаптированный код из ответа следующий:
template<typename T> T& dereference(T &v) { return v; }
template<typename T> const T& dereference(const T &v) { return v; }template <typename T>
typename std::enable_if<!std::is_pointer<T>::value, T&>::type
dereference(T *v) {
return dereference(*v);
}
Однако в этом тесте не удается разыменовать указатель на указатель на тип значения:
template <typename T>
class A
{
public:
bool compare(T a, T b){
return dereference(a) < dereference(b);
}};
int main()
{
int u = 10;
int *v = &u;
int **w = &v;
int i = 5;
int *j = &i;
int **k = &j;
A<int> a;
A<int*> b;
A<int**> c;std::cout << a.compare(i, u) << std::endl;
std::cout << b.compare(j, v) << std::endl;// This fails - 5 < 10 == 0
std::cout << **k << " < " << **w << " == " << c.compare(k, w) << std::endl;return 0;
}
Очевидно, что w
а также k
разыменовываются только один раз, что вызывает operator<
быть вызванным на двух указателях.
Я могу это исправить, добавив следующее:
template <typename T>
typename std::enable_if<!std::is_pointer<T>::value, T&>::type
dereference(T **v) {
return dereference(*v);
}
но тогда это не удастся для int***
,
Есть ли способ сделать это рекурсивно, не добавляя уровни вручную?
Заметка Это просто «теоретический» вопрос.
Это возможно с использованием пользовательских can_dereference
черта характера:
template <typename T>
struct can_dereference_helper {
template <typename U, typename = decltype(*std::declval<U>())>
static std::true_type test(U);
template <typename...U>
static std::false_type test(U...);
using type = decltype(test(std::declval<T>()));
};
template <typename T>
struct can_dereference :
can_dereference_helper<typename std::decay<T>::type>::type {};
и некоторые взаимно-рекурсивные функции с диспетчеризацией тега bit’o:
template <typename T>
auto recursive_dereference(T&& t, std::false_type) ->
decltype(std::forward<T>(t)) {
return std::forward<T>(t);
}
template <typename T>
auto recursive_dereference(T&& t) ->
decltype(recursive_dereference(std::forward<T>(t), can_dereference<T>{}));
template <typename T>
auto recursive_dereference(T&& t, std::true_type) ->
decltype(recursive_dereference(*std::forward<T>(t))) {
return recursive_dereference(*std::forward<T>(t));
}
template <typename T>
auto recursive_dereference(T&& t) ->
decltype(recursive_dereference(std::forward<T>(t), can_dereference<T>{})) {
return recursive_dereference(std::forward<T>(t), can_dereference<T>{});
}
Посмотрите, как это работает вживую в Колиру. Это может показаться излишним по сравнению с ответом Керрека, но я выбрал общий подход, который разыменовывает все, что поддерживает operator*
, Я дам вам решить, какой инструмент лучше всего подходит для вашей задачи.
Вы можете сделать это с чертой, чтобы вычислить окончательный тип возвращаемого значения, здесь названный remove_all_pointers
:
#include <type_traits>
template <typename T> struct remove_all_pointers
{ typedef typename std::remove_reference<T>::type type; };
template <typename T> struct remove_all_pointers<T *>
{ typedef typename std::remove_reference<T>::type type; };
template <typename T>
T & dereference(T & p)
{ return p; }
template <typename U>
typename remove_all_pointers<U>::type & dereference(U * p)
{ return dereference(*p); }
int main(int argc, char * argv[])
{
return dereference(argv);
}
Возможно, вам потребуется добавить CV-варианты; Я все еще думаю об этом.