Вычисление типа указателя на функцию

Учтите следующее:

template<typename T>
struct S
{
typedef M< &T::foo > MT;
}

Это будет работать для:

S<Widget> SW;

где Widget::foo() это какая-то функция

Как бы я изменил определение struct S вместо этого разрешить следующее:

S<Widget*> SWP;

1

Решение

Что вам нужно, это следующее преобразование типа.

  • дано T, вернуть T
  • дано T *, вернуть T

Так получилось, что стандартная библиотека уже реализовала это для нас в std::remove_pointer (хотя это не сложно сделать самому).

С этим вы можете написать

using object_type = std::remove_pointer_t<T>;
using return_type = /* whatever foo returns */;
using MT = M<object_type, return_type, &object_type::foo>;

Что касается вашего комментария о том, что вы также хотите работать с умными указателями, мы должны переопределить преобразование типов.

  • заданный тип умного указателя smart_ptr<T>, вернуть smart_ptr<T>::element_type, который должен быть T
  • данный тип указателя T *, вернуть T
  • в противном случае, учитывая T, вернуть T сам

Для этого нам нужно будет написать собственную мета-функцию. По крайней мере, я не знаю ничего в стандартной библиотеке, которая могла бы здесь помочь.

Начнем с определения основного template (случай «иначе»).

template <typename T, typename = void>
struct unwrap_obect_type { using type = T; };

Второй (анонимный) тип параметра, по умолчанию void будет полезен позже.

Для (сырых) указателей мы предоставляем следующую частичную специализацию.

template <typename T>
struct unwrap_obect_type<T *, void> { using type = T; };

Если бы мы остановились здесь, мы бы в основном получили std::remove_pointer, Но мы добавим дополнительную частичную специализацию для умных указателей. Конечно, сначала мы должны определить, что такое «умный указатель». В этом примере мы будем рассматривать каждый тип с вложенным typedef названный element_type как умный указатель. Настройте это определение по своему усмотрению.

template <typename T>
struct unwrap_obect_type
<
T,
std::conditional_t<false, typename T::element_type, void>
>
{
using type = typename T::element_type;
};

Параметр второго типа std::conditional_t<false, typename T::element_type, void> это запутанный способ моделирования std::void_t в C ++ 14. Идея в том, что у нас есть следующая частичная функция типа.

  • заданный тип T с вложенным typedef названный element_type, вернуть void
  • в противном случае вызвать ошибку замены

Поэтому, если мы имеем дело с умным указателем, мы получим лучшее соответствие, чем основной template В противном случае SFINAE исключит эту частичную специализацию из дальнейшего рассмотрения.

Вот рабочий пример. Агар Т.С. предложил использовать std::mem_fn чтобы вызвать функцию-член. Это делает код намного чище, чем мой первоначальный пример.

#include <cstddef>
#include <functional>
#include <iostream>
#include <memory>
#include <string>
#include <utility>

template <typename ObjT, typename RetT, RetT (ObjT::*Pmf)() const noexcept>
struct M
{
template <typename ThingT>
static RetT
call(ThingT&& thing) noexcept
{
auto wrapper = std::mem_fn(Pmf);
return wrapper(std::forward<ThingT>(thing));
}
};

template <typename T, typename = void>
struct unwrap_obect_type { using type = T; };

template <typename T>
struct unwrap_obect_type<T *, void> { using type = T; };

template <typename T>
struct unwrap_obect_type<T, std::conditional_t<false, typename T::element_type, void>> { using type = typename T::element_type; };

template <typename T>
struct S
{

template <typename ThingT>
void
operator()(ThingT&& thing) const noexcept
{
using object_type = typename unwrap_obect_type<T>::type;
using id_caller_type          = M<object_type, int,                &object_type::id>;
using name_caller_type        = M<object_type, const std::string&, &object_type::name>;
using name_length_caller_type = M<object_type, std::size_t,        &object_type::name_length>;
std::cout << "id:          " << id_caller_type::call(thing)          << "\n";
std::cout << "name:        " << name_caller_type::call(thing)        << "\n";
std::cout << "name_length: " << name_length_caller_type::call(thing) << "\n";
}

};

class employee final
{

private:

int id_ {};
std::string name_ {};

public:

employee(int id, std::string name) : id_ {id}, name_ {std::move(name)}
{
}

int                  id()          const noexcept { return this->id_; }
const std::string&   name()        const noexcept { return this->name_; }
std::size_t          name_length() const noexcept { return this->name_.length(); }

};

int
main()
{
const auto bob = std::make_shared<employee>(100, "Smart Bob");
const auto s_object = S<employee> {};
const auto s_pointer = S<employee *> {};
const auto s_smart_pointer = S<std::shared_ptr<employee>> {};
s_object(*bob);
std::cout << "\n";
s_pointer(bob.get());
std::cout << "\n";
s_smart_pointer(bob);
}
2

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

Других решений пока нет …

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector