Вероятно, самый простой ответ - перегрузить функцию и вызвать один вызов на другой:
template<typename Collection, typename Transform>
void DoIt(const Collection &c, Transform transform)
{
for(auto &item: c)
{
std::cout << transform(item);
}
}
template<typename Collection>
void DoIt(const Collection &c)
{
DoIt(c, [](auto &item) -> decltype(item) { return item; });
}
Или, если по какой-то причине вам действительно нужен только один шаблон, вам понадобится класс, похожий на функцию, который просто проходит через свой аргумент.
namespace std_compat {
struct identity
{
template<typename T>
constexpr T&& operator()(T&& obj) const noexcept
{ return std::forward<T>(obj); }
using is_transparent = void;
};
}
template<typename Collection, typename Transform = std_compat::identity>
void DoIt(const Collection &c, Transform transform = {})
{
for(auto &item: c)
{
std::cout << transform(item);
}
}
Это также позволяет пользователям делать что-то вроде DoIt<decltype(c), std::negate<>>(c);
. Это не кажется особенно хорошим или плохим.
Примечание std::identity
идет в C ++ 20.