Возврат кортежа из функции с использованием единого синтаксиса инициализации
Следующий код компилируется с clang (libС++) и не работает с gcc (libstdС++). Почему gcc (libstdС++) жалуется на список инициализаторов? Я думал, что возвращаемый аргумент использует единый синтаксис инициализации.
std::tuple<double,double> dummy() {
return {2.0, 3.0};
}
int main() {
std::tuple<double,double> a = dummy();
return 0;
}
Ошибка: строка 22: преобразование в 'std:: tuple из инициализатора \ list будет использовать явный конструктор 'constexpr std:: tuple < _T1, _T2 > :: tuple (_U1 & \ &, _U2 &) [с _U1 = double; _U2 = double; = void; _T \ 1 = двойной; _T2 = double]
Примечание: GCC (libstdС++) (и clang (libС++)) принимает
std::tuple<double,double> dummy {1.0, 2.0};
Разве это не тот же случай?
Обновление: это расширение libС++, см. http://llvm.org/bugs/show_bug.cgi?id=15299, а также ответ Говарда Хиннанта ниже.
Ответы
Ответ 1
В отличие от pair<>
, неявное построение a tuple<>
, к сожалению, невозможно. Вы должны использовать make_tuple()
:
#include <tuple>
std::tuple<double,double> dummy()
{
return std::make_tuple(2.0, 3.0); // OK
}
int main()
{
std::tuple<double,double> a = dummy();
return 0;
}
std::tuple
имеет вариационный конструктор, но он помечен как explicit
. Таким образом, он не может использоваться в этой ситуации, когда временное должно быть неявно конструктивным. В пункте 20.4.2 стандарта С++ 11:
namespace std {
template <class... Types>
class tuple {
public:
[...]
explicit tuple(const Types&...); // Marked as explicit!
template <class... UTypes>
explicit tuple(UTypes&&...); // Marked as explicit!
По той же причине запрещено использовать синтаксис инициализации копирования для инициализации кортежей:
std::tuple<double, double> a = {1.0, 2.0}; // ERROR!
std::tuple<double, double> a{1.0, 2.0}; // OK
Или построить ящик неявно при передаче его в качестве аргумента функции:
void f(std::tuple<double, double> t) { ... }
...
f({1.0, 2.0}); // ERROR!
f(make_tuple(1.0, 2.0)); // OK
Соответственно, если вы построите свой std::tuple
явно при возврате его в dummy()
, ошибка компиляции не произойдет:
#include <tuple>
std::tuple<double,double> dummy()
{
return std::tuple<double, double>{2.0, 3.0}; // OK
}
int main()
{
std::tuple<double,double> a = dummy();
return 0;
}
Ответ 2
Ответ, который дает Энди Проул, является правильным. Я хотел прокомментировать реализацию libС++, и формат комментария не позволяет мне достаточно выбора места или форматирования.
Мы с Дэниелом Крюглером поговорили год назад по этому вопросу, и он убедил меня, что этот вопрос заслуживает того, чтобы добавить расширение в libС++, чтобы получить опыт работы на местах. И до сих пор отзывы были положительными. Однако я хочу пояснить: это не так просто, как удаление explicit
из ctor explicit constexpr tuple(UTypes&&...)
.
Даниэль планирует предоставить tuple
конструктор, который отлично соблюдает каждую неявную/явную конструкцию каждого элемента. И , если, каждый элемент будет построен неявно из каждого аргумента в списке инициализаторов, , а затем структура набора неявно, иначе она будет явной.
Например:
Дано:
#include <tuple>
struct A
{
};
struct B
{
B() = default;
B(A);
};
struct C
{
C() = default;
explicit C(A);
};
Тогда:
std::tuple<>
test0()
{
return {}; // ok
}
Ничего особенного сказать об этом. Но и это нормально:
std::tuple<B>
test1B()
{
return {A()}; // ok B(A) implicit
}
потому что преобразование из A
в B
неявно. Однако следующая ошибка времени компиляции:
std::tuple<C>
test1C()
{
return {A()}; // error, C(A) is explicit
}
потому что преобразование из A
в C
является явным. Эта логика продолжается для многоэлементных кортежей. Чтобы неявное преобразование произошло, каждый элемент должен иметь неявное преобразование из списка аргументов:
std::tuple<A, B>
test2B()
{
return {A(), A()}; // ok each element has implicit ctor
}
std::tuple<A, C>
test2C()
{
return {A(), A()}; // error, C(A) is explicit
}
Я должен подчеркнуть: это расширение libС++ в это время.
Обновление
chico сделал хорошее предложение, что я обновляю этот ответ:
Поскольку этот ответ был дан, Даниэль Крюглер написал paper и представил его комитету С++ в Бристоле в апреле этого года. Хотя документ был хорошо принят, он был пересмотрен слишком поздно на этой неделе, чтобы проголосовать за него в текущем рабочем проекте.
Обновление
Предложение Daniel теперь является частью текущего рабочего проекта . В настоящее время реализация libС++ становится стандартной в этом отношении для следующего стандарта С++ (мы надеемся, что на С++ 17).