I can't seem to find a good solution for using SFINAE with variadic template classes.
Let's say I have a variadic template object which doesn't like references:
template<typename... Args>
class NoRef
{
//if any of Args... is a reference, this class will break
//for example:
std::tuple<std::unique_ptr<Args>...> uptrs;
};
And a class which conveniently checks if an argument pack contains references:
template<typename T, typename... Other>
struct RefCheck
{
static const bool value = std::is_reference<T>::value || RefCheck<Other...>::value;
};
template<typename T>
struct RefCheck<T>
{
static const bool value = std::is_reference<T>::value;
};
How do I use this to specialize NoRef for the case where references are present in the arg pack?
With the variadic templates feature, you can define class or function templates that have any number (including zero) of parameters. To achieve this goal, this feature introduces a kind of parameter called parameter pack to represent a list of zero or more parameters for templates.
Variadic templates are class or function templates, that can take any variable(zero or more) number of arguments. In C++, templates can have a fixed number of parameters only that have to be specified at the time of declaration.
Substitution failure is not an error (SFINAE) refers to a situation in C++ where an invalid substitution of template parameters is not in itself an error. David Vandevoorde first introduced the acronym SFINAE to describe related programming techniques.
This doesn't use SFINAE, but essentially does what you intend:
template<bool Ref, typename... Args>
class NoRef_;
template<typename... Args>
class NoRef_<false, Args...>
{
std::tuple<std::unique_ptr<Args>...> uptrs;
};
template<typename... Args>
class NoRef_<true, Args...>
{
// contains reference
};
template<typename... Args>
using NoRef = NoRef_<RefCheck<Args...>::value, Args...>;
// alternative from Nawaz
template<typename... Args> struct NoRef : NoRef_<RefCheck<Args...>::value, Args...> {}
I am afraid this is not possible as-is, because template packs are unwieldy. However, you can pack packs.
// Used to transport a full pack in a single template argument
template <typename... Args> struct Pack {};
We adapt the reference check:
template <typename T, typename... Other>
struct RefCheck
{
static const bool value = std::is_reference<T>::value
|| RefCheck<Other...>::value;
};
template <typename T>
struct RefCheck<T>
{
static const bool value = std::is_reference<T>::value;
};
template <typename... Args>
struct RefCheck<Pack<Args...>>: RefCheck<Args...> {};
And now we can use the Pack
:
template <typename P, bool = RefCheck<P>::value> class NoRef;
template <typename... Args>
class NoRef<Pack<Args...>, false> {
// no reference in Args... here
};
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With