What is the reason for `std::make_tuple`?

Because you cannot use argument deduction for constructors. You need to write explicitly std::tuple<int, double>(i,d);.

It makes it more convenient for creating a tuple and passing it to another function in one-shot.

takes_tuple(make_tuple(i,d)) vs takes_tuple(tuple<int,double>(i,d)).

One less place to change when the type of i or d changes, especially if there were possible conversions to between the old and new types.

If it were possible to write std::tuple(i,d);, make_* would (probably) be redundant.

(Don't ask why here. Maybe for similar reasons why syntax A a(); does not invoke a default constructor. There are some painful c++ syntax peculiarities.)

UPDATE NOTE: As Daniel rightly notices, c++17 will be enhanced, so that template argument deduction will work for constructors, and such delegation will become obsolete.


We can find a rationale for why we need make_tuple and the various other make_* utilities in proposal N3602: Template parameter deduction for constructors which says (emphasis mine):

This paper proposes extending template parameter deduction for functions to constructors of template classes. The clearest way to describe the problem and solution is with some examples.

Suppose we have defined the following.

vector<int> vi1 = { 0, 1, 1, 2, 3, 5, 8 }; 
vector<int> vi2; template<class Func> 
    class Foo() { 
        public: Foo(Func f) : func(f) {} 
        void operator()(int i) { os << "Calling with " << i << endl; f(i); } 
        private: 
        Func func;
    };

Currently, if we want to instantiate template classes, we need to either specify the template parameters or use a "make_*" wrapper, leverage template parameter deduction for functions, or punt completely:

pair<int, double> p(2, 4.5); 
auto t = make_tuple(4, 3, 2.5); 
copy_n(vi1, 3, back_inserter(vi2)); // Virtually impossible to pass a lambda to a template class' constructor
for_each(vi.begin(), vi.end(), Foo<???>([&](int i) { ...}));

Note, the proposal is being tracked via EWG issue 60.


Only for template argument deduction. However, here's a (contrived) example where this is required for using a lambda:

class A
{
public:
    template<typename F>
    A(const std::tuple<F> &t)
    {
        // e.g.
        std::get<0>(t)();
    }
};

class B : public A
{
public:
     B(int i) : A(std::make_tuple([&i]{ ++i; }))
     {
         // Do something with i
     }
};

std::tuple<decltype([&i]{ ++i; })>([&i]{ ++i; }) cannot be used because the two lambda expressions have different types. A polymorphic wrapper like std::function adds runtime overhead. A named class with user-defined operator () would work (which may also need to be a friend of B, depending on the contents of the operator's body). That's what we used in ye olden days before C++11.