Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
menu search
person
Welcome To Ask or Share your Answers For Others

Categories

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 << "";
    }
}

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.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
189 views
Welcome To Ask or Share your Answers For Others

1 Answer

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.

// C++ requires you to type out the same function body three times to obtain
// SFINAE-friendliness and noexcept-correctness. That's unacceptable.
#define RETURNS(...) noexcept(noexcept(__VA_ARGS__)) 
     -> decltype(__VA_ARGS__){ return __VA_ARGS__; }

// The name of overload sets can be legally used as part of a function call -
// we can use a macro to create a lambda for us that "lifts" the overload set
// into a function object.
#define LIFT(f) [](auto&&... xs) RETURNS(f(::std::forward<decltype(xs)>(xs)...))

You can then say:

for_each(t, LIFT(print)); 

Is there a proposal to implicitly convert a template function to a generic constrained lambda?

Yes, look at P0119 or N3617. Not sure about their status.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share

548k questions

547k answers

4 comments

86.3k users

...