Convert template function to generic lambda

点点圈 提交于 2019-11-30 09:49:30

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.

Can I constrain a generic lambda with some enable_if tricks?

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:

#include<vector>
#include<type_traits>
#include<utility>
#include<list>

template<template<typename...> class C, typename... A>
constexpr std::true_type spec(int, C<A...>);

template<template<typename...> class C, template<typename...> class T, typename... A>
constexpr std::false_type spec(char, T<A...>);

int main() {
    auto fn = [](auto&& v) {
        static_assert(decltype(spec<std::vector>(0, std::declval<std::decay_t<decltype(v)>>()))::value, "!");
        // ...
    };

    fn(std::vector<int>{});
    // fn(std::list<int>{});
    //fn(int{});
}

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:

static_assert(decltype(spec<std::vector>(0, std::declval<std::decay_t<decltype(v)>>()))::value, "!");

Add a variable template like the following one:

template<template<typename...> class C, typename T>
constexpr bool match = decltype(spec<C>(0, std::declval<std::decay_t<T>>()))::value;

Then use it in your static_asserts:

static_assert(match<std::vector, decltype(v)>, "!");

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:

auto fn = [](auto&& v) {
    if constexpr(match<std::vector, decltype(v)>) {
        print(v);
    }
};

See your example code running on wandbox.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!