Подтвердить что ты не робот

Есть ли более простой способ добраться до члена класса, завернутого в интеллектуальный указатель?

Извините за загадочное название. Предположим, что это определение:

struct TestNode {
    using CostType = double;
};

Я хотел бы иметь возможность определить шаблон класса следующим образом:

template <typename NodeP,
          typename MyPolicy = /* CostType of the node type corresponding to NodeP */ >
struct TT {
};

В приведенном выше определении NodeP может быть либо простым , либо умным указателем для класса, который определяет CostType, например TestNode. Проблема: как указать значение по умолчанию для параметра шаблона MyPolicy как CostType типа node, соответствующее NodeP?

Вот мое решение:

// like std::remove_pointer, but works with smart pointers as well
template <typename PT> struct my_remove_pointer {
    using type = typename
        std::remove_reference< decltype( *((PT)nullptr) ) >::type;
};

struct TestNode {
    using CostType = double;
};

template <typename NodeP,
          typename MyPolicy = typename my_remove_pointer<NodeP>::type::CostType>
struct TT {
};

Существует ли более простой подход к этой проблеме? В частности, мне не хватает стандартной библиотеки, которая упростит решение?

4b9b3361

Ответ 1

В стандартной библиотеке есть вспомогательный класс pointer_traits. Это похоже на то, что вы хотите.

запустить онлайн

#include <iostream>
#include <memory>
#include <typeinfo>

struct TestNode {
    using CostType = double;
};

template <typename NodeP,
          typename MyPolicy = typename std::pointer_traits<NodeP>::element_type::CostType>
struct TT {
    typedef MyPolicy xxx;
};

int main () {
    TT<TestNode*>::xxx a = 2.8;
    TT<std::unique_ptr<TestNode>>::xxx b = 3.14;
    std::cout << a << std::endl;
    std::cout << b << std::endl;
    return 0;
}

Ответ 2

В некоторых случаях я могу быть немного читабельнее

template<typename T>
using remove_pointer_t = typename my_remove_pointer<T>::type;

template<typename T>
using cost_type_t = typename remove_pointer_t<T>::CostType;

Теперь в вашем коде

template <typename NodeP,
          typename MyPolicy = cost_type_t<NodeP>>
struct TT {
};

Ответ 3

Так как смарт-указатель поддерживает разыменование и .get(), мы можем написать черту типа с помощью void_t, чтобы получить базовый тип:

template <typename... >
using void_t = void;

// base case: not any kind of pointer
template <typename T, typename = void>
struct underlying_type {
    using type = T;
};

// raw pointer
template <typename T, typename = void>
struct underlying_type<T*, void> {
    using type = T;
};

// smart pointer
template <typename T>
struct underlying_type<T, void_t<
                decltype(*std::declval<T>()),
                decltype(std::declval<T>().get())
                >>
{
    using type = std::remove_reference_t<
                     decltype(*std::declval<T>())
                 >;
};

template <typename T>
using underlying_type_t = typename underlying_type<T>::type;

Если вам не нужно поддерживать не указатели (я не уверен), то вы можете просто сделать:

template <typename T>
using underlying_type_t = std::remove_reference_t<decltype(*std::declval<T>())>;

В любом случае, если у вас есть псевдоним:

template <typename NodeP,
          typename MyPolicy = underlying_type_t<NodeP>::Cost>
struct TT { ... };