Both `tuple` and `function` have templated constructors that can take
just about any argument.
One way to disambiguate is to use a templated constructor that checks
the kind of argument (e.g. via a traits class like below) and forwards
to a constructor with explicit mention of the kind of argument:
#include <functional>
#include <tuple>
#include <iostream>
#include <type_traits>
namespace arg {
struct Some_type {};
struct Tuple {};
};
namespace c_arg {
template< class Arg > struct Kind_ { using T = arg::Some_type; };
template< class F > struct Kind_<std::tuple<F>> { using T =
arg::Tuple; };
template< class F > using Kind = typename Kind_<F>::T;
}
struct C {
using Fn = std::function <void (int )>;
Fn mFn;
C( arg::Some_type, Fn fn )
: mFn{ std::move( fn ) }
{ mFn( 1 ); }
C( arg::Tuple, std::tuple <Fn> t )
: mFn{ std::move( std::get<0>( t )) }
{ mFn( 2 ); }
template< class Arg >
C( Arg&& arg )
: C{ c_arg::Kind<std::remove_reference_t<Arg>>{},
std::forward<Arg>( arg ) }
{}
};
auto main()
-> int
{
auto lambda = [](int i) { std::cout << i << std::endl; };
std::tuple<std::function<void(int)>> t{ lambda };
C c1{ lambda };
C c2( t );
}
In practice this technique is used to disambiguate
* rvalue reference versus reference to const, for templated type,
* raw array versus pointer, for templated type.
Cheers & hth.,
- Alf