Perfect shipment and std :: tuple

Consider the following code:

#include <iostream>
#include <tuple>
#include <utility>

// A.
template <typename... Args>
void f (const char* msg, Args&&... args)
{
    std::cout << "A. " << msg << "\n";
}

// B.
template <typename... Args>
void f (const char* msg, std::tuple<Args...>&& t)
{
    std::cout << "B. " << msg << "\n";
}

struct boo
{
    const std::tuple<int, int, long> g () const
    {
        return std::make_tuple(2, 4, 12345);
    }
};

int main ()
{
    f("First", 2, 5, 12345);
    f("Second", std::make_tuple(2, 5, 12345));

    boo the_boo;
    f("Third", the_boo.g());
    f("Fourth", std::forward<decltype(std::declval<boo>().g())>(the_boo.g()));

    return 0;
}

The result of this will be:

A. First
B. Second
A. Third
A. Fourth

From the conclusion it is clear that he does not do what I would like to do, that is, I would like the Third and Fourth to go through the B. version of the function. Std :: forward from the fourth call is superfluous, since there is no perfect transfer there. In order to have an excellent shipping, I know:

  • I should have an rvalue reference in the context of type inference
  • the type of the parameter must be the type of the template for the function

I understand that this will not work. But I do not fully understand:

  • why does the context change with std :: tuple so that it does not work as desired? Why can't a template parameter be a type for another template type?

  • How can I (elegantly) fix this?

+4
1

, const std::tuple, B. .

f, , const std::tuple, Args... const std::tuple. B. , const-, .

, g() .


Edit:

, . std::tuple<Args...>&& , Args..., std::tuple<Args...>&& ; rvalue. , T&&, T.

, :

template <typename T>
struct is_tuple : std::false_type {};

template <typename... Args>
struct is_tuple <std::tuple<Args...>> : std::true_type {};

, :

// B.
template <typename T, typename = typename std::enable_if<
                          is_tuple<typename std::decay<T>::type>::value
                          >::type>
void f (const char* msg, T&& t)
{
    std::cout << "B. " << msg << "\n";
    std::cout << "B. is lval == " << std::is_lvalue_reference<T>() << "\n";
}

, :

//! Tests if T is a specialization of Template
template <typename T, template <typename...> class Template>
struct is_specialization_of : std::false_type {};

template <template <typename...> class Template, typename... Args>
struct is_specialization_of<Template<Args...>, Template> : std::true_type {};

template <typename T>
using is_tuple = is_specialization_of<T, std::tuple>;

is_specialization_of .

!

int main ()
{
    f("First", 2, 5, 12345);
    f("Second", std::make_tuple(2, 5, 12345));

    boo the_boo;
    f("Third", the_boo.g());
    f("Fourth", std::forward<decltype(std::declval<boo>().g())>(the_boo.g()));

    auto the_g = the_boo.g();
    f("Fifth", the_g);

    return 0;
}

:

A. First
B. Second
B. is lval == 0
B. Third
B. is lval == 0
B. Fourth
B. is lval == 0
B. Fifth
B. is lval == 1
+8

All Articles