Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SFINAE with variadic template classes?

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?

like image 281
Andrei Tita Avatar asked Dec 19 '12 09:12

Andrei Tita


People also ask

What is the use of Variadic templates?

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.

What is Variadic template in C++?

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.

What is meant by SFINAE?

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.


2 Answers

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...> {}
like image 59
Pubby Avatar answered Oct 06 '22 03:10

Pubby


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
};
like image 21
Matthieu M. Avatar answered Oct 06 '22 03:10

Matthieu M.