У меня есть служебная функция для преобразования итерации в соответствии с функтором:
template<typename Iterable, typename Functor>
auto transform(Iterable const& input, Functor&& transformer) -> std::vector<decltype(transformer(*input.cbegin()))>
{
std::vector<decltype(transformer(*input.cbegin()))> output;
std::for_each(
std::cbegin(input),
std::cend(input),
[&output, &transformer](auto const& m) { output.emplace_back(transformer(m)); });
return output;
}
void test_transform()
{
std::vector<int> ints { 10, 20, 30 };
auto transformer = [](int i) { return std::to_string(i + 1); };
std::vector<std::string> strs = VectorUtil::transform(ints, transformer);
for (std::string s : strs) { std::cout << s << std::endl; }
}
Довольно некрасиво записывать тип возвращаемого значения дважды. Есть ли способ дать имя:
decltype(transformer(*input.cbegin()))
или
std::vector<decltype(transformer(*input.cbegin()))>
так его можно использовать повторно?
только не указывайте тип возвращаемого значения, его можно вывести godbolt.org/z/WMxahbMx5
Самый красивый вариант — полностью удалить тип возвращаемого значения и объявить его auto
, то есть функция выведет тип возвращаемого значения.
Скорее всего, в вашем случае это нормально, но, возможно, вы не можете этого сделать.
Если вы действительно настаиваете на объявлении возвращаемого типа, все еще есть возможность упростить его, используя шаблон псевдонима.
std::result_of
или std::invoke_result
бесполезны, потому что они не дают вам красивого и короткого типа возврата, и, похоже, в этом вся суть.
template <typename Iterable, typename Functor>
using transform_result_t =
std::vector<decltype(std::declval<Functor&>()(*std::declval<Iterable&>().cbegin()))>;
Такой шаблон псевдонима можно было бы использовать следующим образом:
template<typename Iterable, typename Functor>
auto transform(Iterable const& input, Functor&& transformer)
-> transform_result_t<Iterable, Functor>
{
transform_result_t<Iterable, Functor> output;
// ...
}
Один из способов факторизации типа — поместить его в качестве аргумента шаблона по умолчанию:
template<typename Iterable,
typename Functor,
typename Ret = decltype(std::declval<Functor>()(*std::declval<Iterable>().cbegin()))>
auto transform(Iterable const& input, Functor&& transformer) -> std::vector<Ret>
{
std::vector<Ret> output;
std::for_each(
std::cbegin(input),
std::cend(input),
[&output, &transformer](auto const& m) { output.emplace_back(transformer(m)); });
return output;
}
Спасибо, знание того, как это сделать, будет очень полезно. Думаю, в данном случае std::vector<Ret> transform(...)
будет предпочтительнее. Кстати, в Ret несовпадение скобок.
Я знаю, что есть
std::result_of
, но не знаю, пригодится ли он здесь.