overloaded set of lambdas not working when using variadic template classes

Hi,

The idea is to be able to create an object with several operator() taken from lambda expressions in order to overload it. The idea works fine when we have a definite number of types (callables) but it explotes when we extend it to unlimited number of types. There must be some error in the code for the variadic template case.

First I will show you the one that works:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
	template<typename F1, typename F2>
	struct overload_s : public F1, public F2
	{
		overload_s(F1&& f1, F2&& f2) : F1(std::forward<F1>(f1)), F2(std::forward<F2>(f2)) {}
		overload_s(const F1& f1, F2&& f2) : F1(f1), F2(std::forward<F2>(f2)) {}
		using F1::operator();
		using F2::operator();
	};

	template<typename F1, typename F2>
	auto over(F1&& f1, F2&& f2 )
	{
		return overload_s<F1, F2>(std::forward<F1>(f1), std::forward<F2>(f2));
	}


This works with this code:

1
2
3
4
5
6
7
8
9
10
11
12
13
void useO()
{
	int i = 5;
	double d = 7.3;
	auto l = over(
	([](int* i) {std::cout << "i=" << *i << std::endl; }),
	([](double* d) {std::cout << "d=" << *d << std::endl; })
	);


	l(&i);
	l(&d);
}



The variadic version is like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
template<typename ...F>
struct overload_set;


template<typename F1>
struct overload_set<F1> : public F1
{
	overload_set(F1&& f1) : F1(std::forward<F1>(f1)) {}
	overload_set(const F1& f1) : F1(f1) {}
	using F1::operator();
};

template<typename F1, typename ...F>
struct overload_set<F1, F...> : public F1, public overload_set<F...>
{
	overload_set(F1&& f1, F&&...f) : F1(std::forward<F1>(f1)), overload_set<F...>( std::forward<F>(f)...) {}
	overload_set(const F1& f1, F&&...f) : F1(f1), overload_set<F...>(std::forward<F>(f)...) {}
	using F1::operator();
};

template<typename...F>
auto overload(F&&...f)
{
	return overload_set<F...>(std::forward<F>(f)...);
}



which fails when using this code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void useO()
{
	int i = 5;
	double d = 7.3;
	auto l = overload(
	([](int* i) {std::cout << "i=" << *i << std::endl; }),
	([](double* d) {std::cout << "d=" << *d << std::endl; })
	);

		
	l(&i);
	l(&d);
		
}


I get error:


error C2664: 'void DPWCPP::useO::<lambda_12956753891cefbaefbe3ab21899a617>::operator ()(int *) const': cannot convert argument 1 from 'double *' to 'int *'

message : see declaration of 'DPWCPP::overload_set<DPWCPP::useO::<lambda_12956753891cefbaefbe3ab21899a617>,DPWCPP::useO::<lambda_e14a3e98d7691878a71a96c425684666>>::operator ()'



My impression is that only using int::operator() is working - it is hiding the double::operator(). But then how can I get this to run?


Please help!!

I fixed it!!

I added this code:

 
using overload_set<F...>::operator();


to

1
2
3
4
5
6
7
8
9
	template<typename F1, typename ...F>
	struct overload_set<F1, F...> : public F1, public overload_set<F...>
	{
		overload_set(F1&& f1, F&&...f) : F1(std::forward<F1>(f1)), overload_set<F...>( std::forward<F>(f)...) {}
		overload_set(const F1& f1, F&&...f) : F1(f1), overload_set<F...>(std::forward<F>(f)...) {}
		using F1::operator();
		using overload_set<F...>::operator();
	};



It now works!!


Any remarks?

Any remarks?


C++17 makes this idiom simpler:
1
2
3
4
5
6
7
template <typename... Fs> 
  class overload_set: public Fs... 
  { 
    using Fs::operator()...; 
  };
template <typename... Fs>
  overload_set(Fs...) -> overload_set<Fs...>;


Generally, pass function objects by value. This way a user can opt-in to reference semantics by using std::reference_wrapper.

The standard library mostly follows this guideline. For example, in addition to everything in <algorithm>, std::function takes callable objects by value too.

This snippet doesn't work for function objects that are final.
Last edited on
@mboziz your code does not compile for me in Visual Studio 19 v16.9.1. I had to add a constructor like so:

1
2
3
4
5
6
7
	template<typename...Fs>
	class overload_set17 : public Fs...
	{
	public:
		overload_set17(Fs&&...fs) : Fs(std::forward<Fs>(fs))... {}
		using Fs::operator()...;
	};


Now it works

Why pass function objects by value??
@mbozzi your code does not compile for me in Visual Studio 19 v16.9.1. I had to add a constructor like so:


Sorry, I wrote class where I should have put struct. Otherwise, the type is aggregate, so it does have some usable constructors already.

You can use braces to initialize it:
1
2
3
4
5
6
7
8
9
10
11
12
template <typename... Fs> 
  struct overload_set: public Fs... 
  { 
    using Fs::operator()...; 
  };
template <typename... Fs>
  overload_set(Fs...) -> overload_set<Fs...>;

int main()
{
  auto f = overload_set{[](int){}, [](double){}};   
}

Live demo:
http://coliru.stacked-crooked.com/a/34f09e919691ba12

Why pass function objects by value??

See
https://stackoverflow.com/a/17627391/2085046
Last edited on
Topic archived. No new replies allowed.