Вам необходимо следующее преобразование типа.
- дал
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 удалит эту частичную специализацию из дальнейшего рассмотрения.
Настоящий рабочий пример. T.C. предложил использовать 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);
}
Должно ли оно работать только для * указателей * или указателей и не указателей? – 5gon12eder
Некоторые вещи в вопросе неясны: что такое 'M'? Как «Виджет :: foo» связан с типом, который вы создаете? Что вы пытаетесь достичь? –
В идеале это должно работать как для указателей, так и для не указателей, однако я бы даже оценил версию структуры, которая работала только с типами указателей. – unshul