T.C. gave a clever solution here which used this object:
template<class... Ts> struct overload : Ts... { using Ts::operator()...; };
template<class... Ts> overload(Ts...) -> overload<Ts...>;
struct fallback_t { template<class T> fallback_t(T&&) {} };
Unfortunately I can't get that to compile in Visual Studio 2017. I get the errors:
1> warning C4346: 'Ts::()': dependent name is not a type
1> note: prefix with 'typename' to indicate a type
1> note: see reference to class template instantiation 'owner::overload' being compiled
1> error C2143: syntax error: missing ';' before '...'
1> error C2059: syntax error: '...'
1> error C2238: unexpected token(s) preceding ';'
1> error C2988: unrecognizable template declaration/definition
1> error C2143: syntax error: missing ')' before '...'
1> error C2143: syntax error: missing ';' before '...'
1> error C2365: 'Ts': redefinition; previous definition was 'template parameter'
1> note: see declaration of 'Ts'
1> error C2238: unexpected token(s) preceding ';'
1> error C2059: syntax error: '...'
1> error C2059: syntax error: ')'
1> error C2955: 'owner::overload': use of class template requires template argument list
1> note: see declaration of 'owner::overload'
1> error C2664: 'void owner::bar::
::operator ()(owner::fallback_t) const': cannot convert argument 1 from 'owner::fallback_t' to 'owner::fallback_t'
1> note: use of undefined type 'owner::fallback_t'
1> note: see declaration of 'owner::fallback_t'
1> error C2672: 'owner::findObject': no matching overloaded function found
1> error C2780: 'void owner::findObject(int,const T &)': expects 2 arguments - 1 provided
1> note: see declaration of 'owner::findObject'
Is there any trickery that I can do to make Visual Studio respect the variadic template in?
You can try to compile in c++17 mode with /std::c++17
as @S.M. has noted; note that some C++17 features can break existing code. In addition as @milesbudnek has noted, as of today MSVC doesn't support the requried features.
If you cannot use C++17 mode you can implement this in c++14 in this SO answer.
I'd make a few changes:
template <class... Fs>
struct overload_t;
// zero
template<>
struct overload_t<> {};
// >1
template <class F0, class... Frest>
struct overload_t<F0, Frest...> :
F0,
overload_t<Frest...>
{
overload_t(F0 f0, Frest... rest) :
F0(std::move(f0)), overload_t<Frest...>(std::move(rest)...)
{}
using F0::operator();
using overload_t<Frest...>::operator();
};
// 1
template <class F0>
struct overload_t<F0> : F0
{
overload_t(F0 f0) : F0(std::move(f0)) {}
using F0::operator();
};
template <class... Fs>
auto overload(Fs... fs)
{
return overload_t<Fs...>(std::move(fs)...);
}
It is valid in C++17 only. Try to compile it in Visual Studio 2017 with the flag /std:c++17
under Project -> Properties -> C++ -> Language. Please note, C++17 is available starting from Visual Studio 2017.3.
This is dependent on three separate C++17 features:
- variadic using-declarations (that support pack expansion)
- aggregates with base classes
- class template argument deduction
The usual pre-C++17 workaround for the first is recursive inheritance (forwarding omitted for simplicity):
template<class T, class... Ts>
struct overload : T, overload<Ts...> {
using T::operator();
using overload<Ts...>::operator();
overload(T t, Ts... ts) : T(t), overload<Ts...>(ts...) {}
};
template<class T>
struct overload<T> : T {
using T::operator();
overload(T t) : T(t) {}
};
template<class...Ts>
overload<Ts...> make_overload(Ts... ts) {
return overload<Ts...>(ts...);
}
The constructors are needed if the compiler doesn't support aggregates with base classes; the make_overload
is needed if the compiler doesn't support template argument deduction for class templates.
In C++14, it would be something like:
template <typename ... Ts> struct overload;
template <typename T>
struct overload<T> : T
{
template <typename U>
overload(U&& u) : T(std::forward<U>(u)) {}
using T::operator();
};
template <typename T, typename ... Ts>
struct overload<T, Ts...> : overload<T>, overload<Ts...>
{
template <typename U, typename ... Us>
overload(U&& arg, Us&&... args) : overload<T>(std::forward<U>(arg)),
overload<Ts...>(std::forward<Us>(args)...) {}
using overload<T>::operator();
using overload<Ts...>::operator();
};
template<class... Ts>
overload<std::decay_t<Ts>...> make_overload(Ts&&... args)
{
return {std::forward<Ts>(args)...};
}
Demo