Here I present a first cut of two variants of the template function over(vec, f)
.
Both versions iterate over a vector-like object and call a function object for each element.
One version calls the function object with two arguments - an element reference and an index - the second with just the element reference.
The idea is to get the compiler to select the version that matches the passed-in lambda, so the user can express intent in the lambda signature without having to select a differently-named free function.
here's the code:
#include <vector>
#include <iostream>
template<typename... Ts> struct make_void { typedef void type;};
template<typename... Ts> using void_t = typename make_void<Ts...>::type;
template<class Vector, class F>
auto over(Vector &&vec, F &&f)
-> void_t<decltype(f(vec.operator[](std::declval<std::size_t>()), std::declval<std::size_t>()))>
{
const auto size = vec.size();
for (std::size_t i = 0; i < size; ++i) {
f(vec[i], i);
}
}
template<class Vector, class F>
auto over(Vector &&vec, F &&f)
-> void_t<decltype(f(*vec.begin()))>
{
for (auto &&x : vec) {
f(x);
}
}
int main() {
std::vector<float> vf = {1.0, 1.1, 1.2};
std::cout << "two-argument form:\n";
over(vf, [](auto &&val, auto &&index) {
std::cout << index << " : " << val << std::endl;
});
std::cout << "\none-argument form:\n";
over(vf, [](auto &&val) {
std::cout << val << std::endl;
});
}
Question:
You will see that the clause inside the void_t<>
return type generator knows all about the implementation of the function. I am displeased by this as:
a) it's leaking implementation details in the interface, and
b) it's not DRY.
Is there a better way to achieve this which:
a) allows the implementation to change without changing the template-enabler,
b) doesn't look like my dogs had a play-fight on my keyboard?
In C++17, you might use SFINAE based on
std::is_invocable
, something similar to: