Получить базовый тип любого разыменованного типа C ++ - PullRequest
6 голосов
/ 14 мая 2019

У меня есть функция, которая создает новый объект базового типа P.Здесь P - это разыменовываемый тип, такой как указатель или умный указатель.

template<typename P>
auto make_new()

Например, для указателей и умных указателей

struct A
{
    int a = 3;
};

A* a = make_new<A*>();
std::cout << a->a << std::endl;
delete a;

std::shared_ptr<A> b = make_new<std::shared_ptr<A>>();
std::cout << b->a << std::endl;

Теперь для общих указателей яmake_new будет реализован следующим образом:

template<typename P>
auto make_new()
{
    using Ptype = typename P::element_type;
    return P(new Ptype);
}

, что не работает для указателей.

Теперь, что-то, что работает как для указателей, так и для умных указателей,

template<typename P>
auto make_new()
{
    using Ptype = typename std::remove_reference<decltype(*P())>::type;
    return P(new Ptype);
}

, но не работает для std::optional.

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

Я знаю, что * и -> могут быть перегружены на что угодно, и нет никакой гарантии, что конструктор работает, как указано выше, или имеет смысл сделать.

Просто хочу знать, есть лиэто путь, и я не просто нахожу это, или просто делаю что-то глупое.

1 Ответ

5 голосов
/ 14 мая 2019

Разрешение типа элемента как по указателям, так и по классам

Target. Наша цель - написать шаблон using, который принимает тип с разыменованием в качестве входных данных и возвращает элементтип.

template<class T>
using element_type_t = /* stuff */;

Метод. Мы можем использовать SFINAE, чтобы проверить, есть ли свойство element_type, а если нет, мы вернемся к использованию std::remove_reference<decltype(*P())>().

// This is fine to use in an unevaluated context
template<class T>
T& reference_to(); 

// This one is the preferred one
template<class Container>
auto element_type(int) 
  -> typename Container::element_type;

// This one is the fallback if the preferred one doesn't work
template<class Container>
auto element_type(short) 
  -> typename std::remove_reference<decltype(*reference_to<Container>())>::type;

Получив эту функцию, мы можем написать element_type_t, просто получив тип возвращаемого значения element_type.

// We alias the return type
template<class T>
using element_type_t = decltype(element_type<T>(0)); 

Почему мы не всегда можем получить element_type, разыменовав его? Если вы попытаетесь всегда получить тип значения с помощью оператора *, это может вызвать проблемы с вещамикак итератор для std::vector<bool>, который возвращает объект, который действует как bool, но инкапсулирует битовые манипуляции.В этих случаях тип элемента отличается от типа, возвращаемого разыменованием его.

Определение, принимает ли конструктор указатель или значение

Причина, по которой ваш код не работает с std::optional, заключается в том, что конструктор std::optional принимает само значение, а не указатель назначение.Чтобы определить, какой конструктор нам нужен, мы снова используем SFINAE для определения.

// Base case - use new operator
template<class Container>
auto make_new_impl(int) 
    -> decltype(Container{new element_type_t<Container>})
{
    return Container{new element_type_t<Container>};
}

// Fallback case, where Container takes a value
template<class Container>
auto make_new_impl(long)
    -> decltype(Container{element_type_t<Container>()})
{
    return Container{element_type_t<Container>()};
}

Теперь мы можем написать make_new, чтобы он вызывал make_new_impl:

template<class Container>
auto make_new() {
    return make_new_impl<Container>(0);
}

Пример. Теперь мы можем использовать make_new длясделать либо std::optional, std::shared_ptr, либо даже обычный указатель.

#include <optional>
#include <memory>

int main() {
    // This works
    int* ptr = make_new<int*>(); 

    // This works too
    std::shared_ptr<int> s = make_new<std::shared_ptr<int>>();

    // This also works
    std::optional<int> o = make_new<std::optional<int>>(); 
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...