2016-01-09 5 views
1

Рассмотрим следующее:Вычислительный тип указателя функции

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

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

S<Widget> SW; 

где Widget::foo() есть некоторая функция

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

S<Widget*> SWP; 
+2

Должно ли оно работать только для * указателей * или указателей и не указателей? – 5gon12eder

+3

Некоторые вещи в вопросе неясны: что такое 'M'? Как «Виджет :: foo» связан с типом, который вы создаете? Что вы пытаетесь достичь? –

+0

В идеале это должно работать как для указателей, так и для не указателей, однако я бы даже оценил версию структуры, которая работала только с типами указателей. – unshul

ответ

2

Вам необходимо следующее преобразование типа.

  • дал 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); 
} 
+0

«Обратите внимание, что для того, чтобы действительно вызвать функцию-член, мне пришлось ввести помощника, который условно разделяет указатель». Для этого у нас есть «INVOKE». –

+0

@ T.C. Ну, мы * будем * иметь ... Но даже тогда будет 'std :: invoke' автоматически отвечать' (o. * Pmf)() 'или' (p -> * pmf)() 'в зависимости от того, является ли вызываемый указатель? – 5gon12eder

+1

У нас есть это уже (через 'mem_fn'). И да, он будет обрабатывать разыменование (он делает '((* p). * Pmf)()', так что он даже работает с интеллектуальными указателями и не путается перегрузками '-> *'. Кроме того, вызываемым здесь является PMF, а не объект/указатель. –

Смежные вопросы