Class templates and universal references

In code like this:

template<class...> struct pack{};

template<class, class = int>
struct call_t
{
    template<class... args_t>
    static int apply(args_t&&...)
    { return 0; }
};

template<class... args_t>
struct call_t<pack<args_t...>, // (1)
        std::decay_t<decltype(convert(declval<args_t>()...))> >
{
    template<class... params_t> // (2)
    static int apply(params_t&&... args)
    { return convert(std::forward<params_t>(args)...); }
};

template<class... args_t>
auto test(args_t&&... args) // (3)
{
    return call_t<pack<args_t...> >::
       apply(std::forward<args_t>(args)...);
}

      

This function sends a batch of parameters to some function or whichever function exists convert

and can be called with the passed parameters, keeping intact (I think) their exact passed types, and when its return type int

, regardless of reference or const- classifiers.

I have three doubts with this piece of code.

  • (1) declval

    Is the return type still a generic reference? For example, declval<T>()

    with T = int&

    , its return type will be int&&

    (true r-value reference) or int & &&

    and will it output again as int&

    following the normal rules of universal references when passing to another call? I assumed that this is not the case (as pointed out in 101010), but I do not know in which case to do a great overload test.

  • (2) Does the variational pattern need to be re-specified to use the universal deduction rules, or, since the correct types have already been inferred in (3), do they keep their inferred types inactive?

Or I can just write

template<class... args_t>
struct call_t<pack<args_t...>, // (1)
        std::decay_t<decltype(convert(declval<args_t>()...))> >
{
    // (2)
    static int apply(args_t... args)
    { return convert(args...); }
};

      

?

The template class call_t

is an implementation detail, so it will only be created internally test

.

+3


source to share


1 answer


These two cases are not equivalent . This example:

template<class... args_t>
struct call_t<pack<args_t...>, // (1)
        std::decay_t<decltype(convert(declval<args_t>()...))> >
{
    // (2)
    static int apply(args_t... args)
    { return convert(args...); }
};

      

does not forward anything. The parameter args...

bundle is lvalues ​​because they have names. This is very different from this code:

template<class... params_t> // (2)
static int apply(params_t&&... args)
{ return convert(std::forward<params_t>(args)...); }

      



in which it is args...

redirected. The resulting behavior is that the new example may be slower than the old example (make copies instead of moves), or just surprisingly not compile. Let's consider convert(std::unique_ptr<int> )

. It can be done with args_t = {std::unique_ptr<int>}

, but the internal apply()

will fail because you try to copy unique_ptr

.

You will need to do:

static int apply(args_t... args)
{ return convert(std::forward<args_t>(args)...); }

      

+1


source







All Articles