Невозможно использовать std :: function в качестве типа параметра (нужна версия указателя на функцию), скорее шаблон, как STL, но тогда он не может вывести аргументы - PullRequest
0 голосов
/ 08 февраля 2019

Следующий код был опробован в Coliru с аргументами компиляции по умолчанию

g ++ -std = c ++ 17 -O2 -Wall -pedantic -pthread main.cpp && ./a.out

Предположим, мы хотим создать оболочку, которая удерживает «контейнер» данных (например, vector, list, любую структуру данных, которая содержит одно значение, которое может быть шаблонизировано).Затем мы хотим преобразовать его в другой тип с помощью вспомогательной функции convert.Эту функцию можно часто вызывать, было бы неплохо просто написать convert(...), а не шаблонировать ее как convert<TypeToConvertTo>(...).В не MCVE я могу сделать это, но это требует абсолютно некрасивой копии-пасты std::function параметров и параметров указателя функции ... что нехорошо (если это не необходимое зло? Надеюсь, нет)

Также предположим, что у нас это есть (обратите внимание, что такие вещи, как std::move, explicit, копирование в функцию преобразования с помощью std :: copy () или что-то в этом роде и т. Д.)

Попытка 1: при использовании std::function ниже происходит сбой:

#include <functional>
#include <iostream>
#include <string>
#include <type_traits>
#include <vector>

using std::string;
using std::vector;

template <template<typename...> class DataContainer, typename T, typename... TArgs>
struct Wrapper {
    DataContainer<T, TArgs...> dataContainer;

    Wrapper(DataContainer<T, TArgs...> container) : dataContainer(container) { }

    // Using parameter type std::function<V(T)> fails here
    template <typename V, typename... VArgs>
    DataContainer<V, VArgs...> convert(std::function<V(T)> f) {
        DataContainer<V, VArgs...> output;
        for (T t : dataContainer)
            output.emplace_back(f(t));
        return output;
    }
};

int main() {
    vector<int> nums = {123};
    Wrapper w{nums};

    vector<string> intVec = w.convert(std::to_string);

    std::cout << intVec.front() << std::endl;
}

Не компилируется и выдает ошибку

main.cpp: В функции 'int main ()':

main.cpp: 37: 57: ошибка: нет соответствующей функции для вызова 'Wrapper> :: convert ()'

     vector<string> intVec = w.convert(std::to_string);

                                                     ^

main.cpp: 17: 36: note:андидат: 'шаблон DataContainer Wrapper :: convert (std :: function) [с V = V;VArgs = {VArgs ...};DataContainer = std :: vector;T = int;TArgs = {std :: allocator}] '

     DataContainer<V, VArgs...> convert(std::function<V(T)> f) {

                                ^~~~~~~

main.cpp: 17: 36: примечание: сбой вывода / замены аргумента шаблона:

main.cpp: 37: 57:примечание: несовпадающие типы 'std :: function' и 'std :: __ cxx11 :: string () (long double)' {aka 'std :: __ cxx11 :: basic_string () (long double)'}

     vector<string> intVec = w.convert(std::to_string);

                                                     ^

main.cpp: 37: 57: примечание: несовпадающие типы 'std :: function' и 'std :: __ cxx11 :: string () (int)' {aka 'std:: __cxx11 :: basic_string () (int) '}

main.cpp: 37: 57: примечание: не удалось вывести параметр шаблона' V '

Iпопытался создать свою собственную автономную статическую функцию в случае, если с ней возникают перегрузки, но я получаю такую ​​же ошибку, говоря:

несоответствующие типы 'std :: function' и 'int () (std :: __ cxx11 :: string) '{aka' int () (std :: __ cxx11 :: basic_string) '}

Однако, когда я изменяю std::functionна указатель на функцию с:

// Now using V(*f)(T) instead
template <typename V, typename... VArgs>
DataContainer<V, VArgs...> convert(V(*f)(T)) {
    DataContainer<V, VArgs...> output;
    for (T t : dataContainer)
        output.emplace_back(f(t));
    return output;
}

Это работает и теперь печатает

123

ThЭто дает мне то, что я хочу, но теперь я должен указать тип при преобразовании, например .convert<TypeHere>(...), который я хотел бы, чтобы компилятор сделал для меня.Возможно ли это?

Попытка 2:

Я думал, что смогу сделать то, что делает библиотека STL, где она шаблонирует функцию следующим образом:

template <typename Func, typename V, typename... VArgs>
DataContainer<V, VArgs...> convert(Func f) {
    DataContainer<V, VArgs...> output;
    for (T t : dataContainer)
        output.emplace_back(f(t));
    return output;
}

, но затем происходит сбой компиляции с:

main.cpp: в функции 'int main ()':

main.cpp: 45: 57: ошибка: нет соответствующей функциидля вызова 'Wrapper> :: convert ()'

     vector<string> intVec = w.convert(std::to_string);

                                                     ^

main.cpp: 33: 36: примечание: кандидат: 'шаблон DataContainer Wrapper :: convert (Func) [with Func = Func;V = V;VArgs = {VArgs ...};DataContainer = std :: vector;T = int;TArgs = {std :: allocator}] '

     DataContainer<V, VArgs...> convert(Func f) {

                                ^~~~~~~

main.cpp: 33: 36: примечание: сбой вывода / замены аргумента шаблона:

main.cpp: 45: 57:примечание: не удалось вывести параметр шаблона 'Func'

     vector<string> intVec = w.convert(std::to_string);

                                                     ^

Все, что я хочу, - это чтобы строка

vector<string> intVec = w.convert(std::to_string);

работала без необходимости писать в какие-либо шаблоны дляправо на convert.

Что я делаю не так?Я могу получить то, что хочу, с помощью указателей на функции, но тогда кажется, что мне нужно написать кучу перегрузок, таких как V(*f)(T), V(*f)(const &T)

Попытка 3:

Лямбды не работают, пока я не укажу тип, то есть я должен сделать это:

vector<string> intVec = w.convert<string>([](int i) { return std::to_string(i); });

и я хочу:

vector<string> intVec = w.convert([](int i) { return std::to_string(i); });

, что происходит, когда я не указываютип (почему он не может это вывести?)

main.cpp: в функции 'int main ()':

main.cpp: 45: 82: ошибка: отсутствует функция сопоставления для вызова 'Wrapper> :: convert (main () ::)'

     vector<string> intVec = w.convert([](int i) { return std::to_string(i); });

                                                                              ^

main.cpp: 17: 36:примечание: кандидат: 'шаблон DataContainer Wrapper :: convert (std :: function) [с V = V;VArgs = {VArgs ...};DataContainer = std :: vector;T = int;TArgs = {std :: allocator}] '

     DataContainer<V, VArgs...> convert(std::function<V(T)> f) {

                                ^~~~~~~

main.cpp: 17: 36: примечание: сбой вывода / замены аргумента шаблона:

main.cpp: 45: 82:примечание: 'main () ::' не является производным от 'std :: function'

     vector<string> intVec = w.convert([](int i) { return std::to_string(i); });

                                                                              ^

Короче говоря, все это делается, потому что я должен копировать каждый разфункция, которая делает подобные вещи с указателем на функцию и перегрузкой std :: function, а затем, если я хочу поддержать аргументы функции, которые являются rvalues, lvalues, const lvalues ​​... Я получаю комбинаторный взрыв функций, и этопревращение рабочего решения в беспорядок.Я не знаю, неизбежно ли это, или я что-то упускаю, чтобы получить вывод типа, позволяя мне передавать либо имя функции, либо лямбду.

1 Ответ

0 голосов
/ 08 февраля 2019

Это ошибка, которую все постоянно совершают:

template <typename V>
DataContainer<V> convert(std::function<V(T)> f);

Я понимаю, почему люди так поступают, это просто правильный способ сделать это правильно?Даже писал об этом подробно здесь .Ключ: вывод std::function это всегда неправильно .Вы не можете вывести это, потому что это не разрешит преобразования, и даже если вы можете вывести это, это добавляет накладные расходы, которые вы не хотите.

К сожалению, не существует такого же чистого языкового решения.И под чистым я имею в виду, что T и V так четко связаны друг с другом, как в нерешении выше.

То, что вы действительно хотите сделать, таково:

template <typename F, typename V = std::invoke_result_t<F, T>>
DataContainer<V> convert(F f);

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

Проблема с std::to_string совершенно отдельная - вы просто не можете передать перегруженную функцию или шаблон функции в шаблон функции.Вы всегда должны обернуть это в лямбду.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...