Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
227 views
in Technique[技术] by (71.8m points)

c++ - C++11 "overloaded lambda" with variadic template and variable capture

I'm investigating a C++11 idiom which might be called "overloaded lambda":

Overloading n functions with variadic template seemed very appealing to me but it turned out it didn't work with variable capture: any of [&] [=] [y] [&y] (and [this] etc if in a member function) lead to compilation failure: error: no match for call to '(overload<main(int, char**)::<lambda(int)>, main(int, char**)::<lambda(char*)> >) (char*&)' (with my local GCC 4.9.1 and ideone.com GCC 5.1)

On the other hand, the fixed 2-ary case didn't suffer that problem. (Try changing the first #if 0 to #if 1 on ideone.com)

Any ideas on what's happening here? Is this a compiler bug, or am I deviating from the C++11/14 spec?

http://ideone.com/dnPqBF

#include <iostream>
using namespace std;

#if 0
template <class F1, class F2>
struct overload : F1, F2 {
  overload(F1 f1, F2 f2) : F1(f1), F2(f2) { }

  using F1::operator();
  using F2::operator();
};

template <class F1, class F2>
auto make_overload(F1 f1, F2 f2) {
  return overload<F1, F2>(f1, f2);
}
#else
template <class... Fs>
struct overload;

template <class F0, class... Frest>
struct overload<F0, Frest...> : F0, overload<Frest...> {
  overload(F0 f0, Frest... rest) : F0(f0), overload<Frest...>(rest...) {}

  using F0::operator();
};

template <>
struct overload<> {
  overload() {}
};

template <class... Fs>
auto make_overload(Fs... fs) {
  return overload<Fs...>(fs...);
}
#endif

#if 0
#define CAP
#define PRINTY()
#else
#define CAP y
#define PRINTY() cout << "int y==" << y << endl
#endif

int main(int argc, char *argv[]) {
    int y = 123;

    auto f = make_overload(
        [CAP] (int x) { cout << "int x==" << x << endl; PRINTY(); },
        [CAP] (char *cp) { cout << "char *cp==" << cp << endl; PRINTY(); });
    f(argc);
    f(argv[0]);
}
See Question&Answers more detail:os

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

1 Reply

0 votes
by (71.8m points)

Overload resolution works only for functions that exist in a common scope. This means that the second implementation fails to find the second overload because you don't import function call operators from overload<Frest...> into overload<F0, Frest...>.

However, a non-capturing lambda type defines a conversion operator to a function pointer with the same signature as the lambda's function call operator. This conversion operator can be found by name lookup, and this is what gets invoked when you remove the capturing part.

The correct implementation, that works for both capturing and non-capturing lambdas, and that always calls operator() instead of a conversion operator, should look as follows:

template <class... Fs>
struct overload;

template <class F0, class... Frest>
struct overload<F0, Frest...> : F0, overload<Frest...>
{
    overload(F0 f0, Frest... rest) : F0(f0), overload<Frest...>(rest...) {}

    using F0::operator();
    using overload<Frest...>::operator();
};

template <class F0>
struct overload<F0> : F0
{
    overload(F0 f0) : F0(f0) {}

    using F0::operator();
};

template <class... Fs>
auto make_overload(Fs... fs)
{
    return overload<Fs...>(fs...);
}

DEMO

In , with class template argument deduction and pack expansion of using declarations in place, the above implementation can be simplified to:

template <typename... Ts> 
struct overload : Ts... { using Ts::operator()...; };

template <typename... Ts>
overload(Ts...) -> overload<Ts...>;

DEMO 2


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...