Вывести возвращаемый тип функции-члена

В функции шаблона я пытался создать std::vector с его value_type, зависящим от функции-члена параметра шаблона для функции. Этот параметр шаблона ограничен тем, что является вектором, содержащим уникальные указатели определенного типа с определенной функцией. Например:

/* somewhere in the code */
std::vector< std::unique_ptr< Widget > > myVec;
/* work with myVec and fill it, then call the relevant function */
func(myVec);

Теперь функции func необходимо получить возвращаемый тип функции-члена member_func из Widget. Обратите внимание, что Widget также может быть другого типа, если он имеет функцию-член member_func.

template <typename Vec>
void func(const Vec& vec) {
  using ret_type = decltype(Vec::value_type::element_type::member_func()); // Doesn't work
  std::vector< ret_type > local_vec;
}

Я пробовал разные вещи, например. std::result_of, std::invoke_result и decltype, но у меня не получается. Возможно ли это вообще, и если да, то как этого можно достичь?


person KorbenDose    schedule 06.06.2018    source источник
comment
Вы можете вызывать функцию-член только для экземпляров этого класса, а не для типа класса (что и есть ...::element_type). std::declval, используемый в ответе, представляет собой идиоматический способ получить экземпляр типа в невычисленном (например, decltype) контексте.   -  person Max Langhof    schedule 06.06.2018


Ответы (1)


Это близко к тому, что вы хотели?

#include <vector>
#include <utility>
#include <memory>

struct Foo
{
    int member_func();
};

template <typename Vec>
void func(const Vec& vec) {

    using ret_type = decltype(std::declval<typename Vec::value_type>()->member_func());

    std::vector< ret_type > local_vec;
}


int main()
{
    std::vector<std::unique_ptr<Foo>> v;
    func(v);
}

Демонстрация: https://godbolt.org/g/dJkSf1

Объяснение:

std::declval<typename Vec::value_type>() создает ссылку на unique_ptr (который должен использоваться в невычисленном контексте). Затем мы берем decltype вызова generated_reference->member_function().

Это будет тот же тип, что и результат vec[0]->member_func()

Действительно, мы могли бы написать это так:

template <typename Vec>
void func(const Vec& vec) {

    using ret_type = decltype(vec.at(0)->member_func());

    std::vector< ret_type > local_vec;
}

Что может быть более выразительным и универсальным (теперь Vec может быть любым типом, похожим на вектор и содержащим указатели на Foo)

Более того, чем более общий подход к выводу мы делаем, тем более общей становится наша func функция:

#include <vector>
#include <utility>
#include <memory>
#include <set>
#include <iterator>

struct Foo
{
    int member_func();
};

template <typename Vec>
void func(const Vec& vec) {

    using ret_type = decltype((*std::begin(vec))->member_func());

    std::vector< ret_type > local_vec;
}


int main()
{
    std::vector<std::unique_ptr<Foo>> v;
    func(v);
    func(std::array<std::unique_ptr<Foo>, 10> { });

    Foo* foos[] = { nullptr, nullptr };
    func(foos);

    func(std::set<std::shared_ptr<Foo>, std::owner_less<>> {});
}

Примечание

Этот код предполагает, что return_type для Foo::member_func не является ссылочным типом.

Если это возможно, нам нужно решить, использовали ли мы метапрограммирование для:

а) преобразовать ссылочные типы в std::reference_wrapper, чтобы их можно было хранить в векторе, или

б) преобразовать ссылочные типы в основные типы с помощью std::decay, что приведет к созданию копий.

person Richard Hodges    schedule 06.06.2018
comment
это может быть полезно для decay_t ret_type, так как std::vector не любит const и ссылку в типе. - person Jarod42; 06.06.2018
comment
@Jarod42 добавил примечание - person Richard Hodges; 06.06.2018
comment
Хорошо, теперь я понял. Мне нравится более общий подход, и я остановился на using ret_type = std::decay_t<decltype((*std::begin(vec))->member_func())>;. Спасибо! - person KorbenDose; 06.06.2018
comment
@KorbenDose Я согласен, я думаю, что это самое выразительное. Рад, что помог. - person Richard Hodges; 06.06.2018