Следующий код был опробован в 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 ... Я получаю комбинаторный взрыв функций, и этопревращение рабочего решения в беспорядок.Я не знаю, неизбежно ли это, или я что-то упускаю, чтобы получить вывод типа, позволяя мне передавать либо имя функции, либо лямбду.