`std::make_tuple` 的原因是什么?

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

我的意思是为什么 std::make_tuple 存在?我知道在某些情况下,该函数会减少您必须键入的字符数量,因为您可以避免使用模板参数。但这是唯一的原因吗?为什么 std::tuple 有此功能而其他 class 模板没有此功能?仅仅是因为在这种情况下你可能会更频繁地使用std::tuple吗?


这里有两个示例,其中 std::make_tuple 减少了字符数:

// Avoiding template parameters in definition of variable.
// Consider that template parameters can be very long sometimes.
std::tuple<int, double> t(0, 0.0); // without std::make_tuple
auto t = std::make_tuple(0, 0.0);  // with std::make_tuple

// Avoiding template parameters at construction.
f(std::tuple<int, double>(0, 0.0)); // without std::make_tuple
f(std::make_tuple(0, 0.0));         // with std::make_tuple

但是就像上面写的那样,许多其他 class 模板没有这样的功能。

因为你不能对构造函数使用参数推导。您需要明确地写 std::tuple<int, double>(i,d);.

这使得创建元组并一次性将其传递给另一个函数更加方便。

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

id 的类型发生变化时,需要更改的地方少了一个,尤其是在新旧类型之间可能存在转换的情况下。

如果可以写成 std::tuple(i,d);make_* 将(可能)是多余的。

(不要在这里问为什么。也许出于类似的原因语法 A a(); 不调用默认构造函数。有一些令人痛苦的 c++ 语法特性。)

更新说明: 正如 Daniel 正确注意到的那样,c++17 将得到增强,因此模板参数推导将适用于构造函数,并且这种委托将变得过时。

我们可以在提案 N3602: Template parameter deduction for constructors 中找到为什么我们需要 make_tuple 和其他各种 make_* 实用程序的基本原理,其中说(强调我的):

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) { ...}));

请注意,提案正在通过 EWG issue 60 进行跟踪。

仅用于模板参数推导。但是,这是一个(人为的)示例,其中这是使用 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; })不能用,因为两个lambda表达式的类型不同。像 std::function 这样的多态包装器会增加运行时开销。具有用户定义的 operator () 的命名 class 将起作用(可能还需要成为 B 的友元,具体取决于运算符正文的内容)。这就是我们在 C++11 之前的旧时代使用的东西。

我认为这种函数的巧妙用法是作为参数传递。
类似的东西:

std::bind_front(&std::make_tuple, 1, "test", true);

它可能很有用,因为如果我没记错的话,我们不能直接调用构造函数。

auto obj = Object::Object(3); // error: cannot call constructor ‘Object::Object’ directly [-fpermissive]