В чем причина `std:: make_tuple`?
Я имею в виду, почему существует std::make_tuple
? Я знаю, что есть ситуации, когда функция уменьшает количество символов, которые вы набираете, потому что вы можете избежать параметров шаблона. Но это единственная причина? Что делает std::tuple
особенным, что функция существует, в то время как другие шаблоны классов не имеют такой функции? Это только потому, что вы можете чаще использовать 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
Но, как написано выше, у вас нет такой функции для многих других шаблонов классов.
Ответы
Ответ 1
Потому что вы не можете использовать вывод аргумента для конструкторов. Вам нужно явно написать std::tuple<int, double>(i,d);
.
Это делает его более удобным для создания кортежа и передачи его другой функции в один кадр.
takes_tuple(make_tuple(i,d))
vs takes_tuple(tuple<int,double>(i,d))
.
Уменьшение места при изменении типа i
или d
, особенно если возможны переходы между старым и новым типами.
Если бы можно было написать std::tuple(i,d);
, make_*
было бы (вероятно) избыточным.
(Не спрашивайте, почему здесь. Возможно, по тем же причинам, почему синтаксис A a();
не вызывает конструктор по умолчанию. Существуют некоторые болезненные особенности синтаксиса С++.)
ОБНОВЛЕНИЕ ПРИМЕЧАНИЕ:
Поскольку Daniel правильно распознает, С++ 17 будет расширен, поэтому вывод шаблона будет работать для конструкторов, и такое делегирование устареет.
Ответ 2
Мы можем найти обоснование того, зачем нам нужны make_tuple
и другие другие make_ * утилиты в предложении N3602: вычитание параметров шаблона для конструкторов в котором говорится (акцент мой):
В этом документе предлагается расширить вычет параметра шаблона для функций конструкторам шаблонных классов. Самый простой способ описать проблему и решение - это некоторые примеры.
Предположим, что мы определили следующее.
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;
};
В настоящее время, если мы хотим создать экземпляры классов шаблонов, нам нужно указать параметры шаблона или использовать "make_ *" оболочку, вывести аргумент шаблона шаблона для функций или полностью punt:
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.
Ответ 3
Только для вывода аргумента шаблона. Однако здесь (надуманный) пример, где это необходимо для использования лямбда:
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; })
не может использоваться, потому что два лямбда-выражения имеют разные типы. Полиморфная оболочка, такая как std::function
, добавляет служебные данные во время выполнения. Будет работать именованный класс с пользовательским operator ()
(который также может быть другом B
, в зависимости от содержимого тела оператора). Это то, что мы использовали в древние времена до С++ 11.