I'd like to pass templated functions around as if they were generic lambdas, however this does not work.
#include <iostream>
#include <vector>
#include <tuple>
#include <string>
#include <utility>
// for_each with std::tuple
// (from https://stackoverflow.com/a/6894436/1583122)
template<std::size_t I = 0, typename FuncT, typename... Tp>
inline typename std::enable_if<I == sizeof...(Tp), void>::type
for_each(std::tuple<Tp...> &, FuncT)
{}
template<std::size_t I = 0, typename FuncT, typename... Tp>
inline typename std::enable_if<I < sizeof...(Tp), void>::type
for_each(std::tuple<Tp...>& t, FuncT f) {
f(std::get<I>(t));
for_each<I + 1, FuncT, Tp...>(t, f);
}
// my code
template<class T> auto
print(const std::vector<T>& v) -> void {
for (const auto& e : v) {
std::cout << e << "\t";
}
}
struct print_wrapper {
template<class T>
auto operator()(const std::vector<T>& v) {
print(v);
}
};
auto print_gen_lambda = [](const auto& v){ print(v); };
auto print_gen_lambda_2 = []<class T>(const std::vector<T>& v){ print(v); }; // proposal P0428R1, gcc extension in c++14/c++17
int main() {
std::tuple<std::vector<int>,std::vector<double>,std::vector<std::string>> t = { {42,43},{3.14,2.7},{"Hello","World"}};
for_each(t, print); // case 1: error: template argument deduction/substitution failed: couldn't deduce template parameter 'FuncT'
for_each(t, print_wrapper()); // case 2: ok
for_each(t, print_gen_lambda); // case 3: ok
for_each(t, print_gen_lambda_2); // case 4: ok
}
Note that case 2 and 4 are strictly equivalent. Case 3 is more general but unconstrained (this is a problem for me). I think that case 1 should be treated equivalently to cases 2 and 4 by the language, however this is not the case.
- Is there a proposal to implicitly convert a template function to a generic constrained lambda (case 2/4)? If no, is there a fundamental language reason that prevents from doing so?
- As of now, I have to use case 2, which is quite cumbersome.
- case 4: not c++14-compliant, even if should be standard in c++20, and still not perfect (verbose since you create a lambda that fundamentally does not add any information).
- case 3: is unconstrained, but I rely (not shown here) on substitution failure for calls to "print" with non-"vector" arguments (P0428R1 mentions this problem). So I guess the subsidiary question is "Can I constrain a generic lambda with some enable_if tricks?"
Is there, in C++14/17/20, a very terse manner to enable the conversion from case 1 to case 2? I am even open to macro hacks.
Yes.
You can then say:
Yes, look at P0119 or N3617. Not sure about their status.
If all what you want is to constrain the types of the parameters of your generic lambda, you can do that with a couple of function declarations (no definition required) and a
static_assert
(so that you get a graceful message error at compile-time in case of failure). No macro at all around (they are so C-ish).It follows a minimal, working example:
If you toggle the comments to the last lines, the
static_assert
will throw an error and the compilation will fail as expected.See it up and running on wandbox.
Side note.
Of course you can reduce the boilerplate here:
Add a variable template like the following one:
Then use it in your
static_assert
s:Pretty clear, isn't it?
Note.
In C++17 you'll be able to reduce even more the code required to do that by defining your lambda as:
See your example code running on wandbox.