Возврат кортежа из функции с использованием единого синтаксиса инициализации

Следующий код компилируется с помощью clang (libc ++) и завершается ошибкой с gcc (libstdc ++). Почему gcc (libstdc ++) жалуется на список инициализаторов? Я думал, что возвращаемый аргумент использует единый синтаксис инициализации.

std::tuple dummy() {
  return {2.0, 3.0};
}

int main() {   
  std::tuple a = dummy();   
  return 0;
}

Ошибка: строка 22: преобразование в ‘станд :: кортеж» из инициализатора \ списка будет использовать явный конструктор ‘constexpr std :: tuple <_T1, _T2>:: кортеж (_U1 & \ &, _U2 &&) [с _U1 = double; _U2 = двойной; = пустота; _T \ 1 = double; _T2 = double] ’

Замечания: GCC (libstdc ++) (и clang (libc ++)) принимают

std::tuple dummy {1.0, 2.0};

Isn»Тоже самое?

Обновить: это расширение libc ++, см.http://llvm.org/bugs/show_bug.cgi?id=15299 а также ответит Говард Хиннант ниже.

 Liviu22 авг. 2016 г., 11:02
Также Visual Studio 14 Update 3 принимает его.
 sellibitze20 февр. 2013 г., 15:11
Просто комментируя ваш последний вопрос: Нет, этоэто не тот же случай. Оператор возврата "копия инициализации " контекст, тогда как ваш последний пример на самом деле "прямая инициализация ", Разница между ними заключается в том, что для инициализации копии рассматриваются только неявные конструкторы.

Ответы на вопрос(2)

Решение Вопроса

В отличие отpairнеявная конструкцияtuple К сожалению, это невозможно. Вы должны использовать:make_tuple()

#include 

std::tuple dummy()
{
    return std::make_tuple(2.0, 3.0); // OK
}

int main() 
{   
    std::tuple a = dummy();   
    return 0;
}

std::tuple имеет конструктор переменных, но он помечен какexplicit, Таким образом, он не может быть использован в этой ситуации, когда временное должно быть неявно конструируемым. Согласно пункту 20.4.2 стандарта C ++ 11:

namespace std {
    template 
    class tuple {
    public:

        [...]
        explicit tuple(const Types&...); // Marked as explicit!

        template 
        explicit tuple(UTypes&&...);     // Marked as explicit!

По той же причине недопустимо использовать синтаксис копирования-инициализации для инициализации кортежей:

std::tuple a = {1.0, 2.0}; // ERROR!
std::tuple a{1.0, 2.0}; // OK

Или создать неявный кортеж при передаче его в качестве аргумента функции:

void f(std::tuple t) { ... }
...
f({1.0, 2.0}); // ERROR!
f(make_tuple(1.0, 2.0)); // OK

Соответственно, если вы строите свойstd::tuple явно при возврате вdummy(), ошибка компиляции не произойдет:

#include 

std::tuple dummy()
{
    return std::tuple{2.0, 3.0}; // OK
}

int main() 
{   
    std::tuple a = dummy();   
    return 0;
}
 gnzlbg19 февр. 2013 г., 17:08
@AndyProwl Спасибо! Вы очень полезны!
 Andy Prowl19 февр. 2013 г., 17:12
@gnzlbg: рад, что помог :-)
 Jonathan Wakely19 февр. 2013 г., 17:29
Еслиpair а такжеtuple были разработаны в то же время теми же людьми, они, вероятно, были бы более последовательными (и, возможно,pair будет иметь явный конструктор!) но они не былит, и они нет (и это нет)
 Yakk - Adam Nevraumont19 февр. 2013 г., 19:18
Сделать конструктор неявным для арностей> 1 означает, что вариационный код будет работать и компилироваться до тех пор, пока не будет передан кортеж арности 0 или 1. Имея способ сказать «я»м, используя явный конструктор здесь " без необходимости повторять Typename неу меня нет этой проблемы ...
 Jonathan Wakely19 февр. 2013 г., 17:15
@PlasmaHH, это было быcplusplus.github.com/LWG/lwg-active.html#2051 (который я не поддерживаю FWIW, яскорее иметь языковую поддержку для вызова явных конструкторов в операторе возврата, напримерreturn auto{1,2,3} или что-то еще, что применимо больше, чем просто)tuple
 Andy Prowl19 февр. 2013 г., 17:17
@JonathanWakely: Могу я спросить, в чем причина, по которой он не поддерживается? Это работает дляstd::pairтак что я думаю, что было бы естественно обобщить это сstd::tuple
 gnzlbg19 февр. 2013 г., 17:03
Так что лязг это неправильно, чтобы принять это? Я думал, что возвращаемый тип может быть инициализирован с помощью равномерной инициализации (не список инициализатора).
 Matthieu M.19 февр. 2013 г., 17:25
@JonathanWakely: хотя я согласен, чтоreturn заявлениеexplicit Достаточно выбрать тип w.r.t, я до сих пор не вижу причин для конструктораstd::tuple бытьexplicit для артерий> 1.
 Jonathan Wakely19 февр. 2013 г., 17:20
Я думал, что просто дал обоснование :) Если желаемый вариант использованияreturn {a,b,c}; Я предпочел бы иметь более общее решение, которое позволяет инициализации списка вызывать явные конструкторы в операторах возврата без необходимости измененияstd::tuple, Мне нравится иметьexplicit конструкторы, я нене хочу выбирать между ними иreturn {...};, Я хочу оба.
 gnzlbg19 февр. 2013 г., 17:07
Хорошо яперефразирую вопрос тогда. Тогда неправильно ли libc ++ принимать это?
 Andy Prowl19 февр. 2013 г., 17:06
@gnzlbg: Дело не в компиляторе, а в реализации стандартной библиотеки. Clang 3.2 не компилирует этоВот.
 Jonathan Wakely19 февр. 2013 г., 17:38
Да, я согласен, что иногда это будет полезно. Многие из моих использованийtuple находятся в шаблонах с переменным числом аргументов, где арность может быть равна нулю, одному или нескольким, поэтому даже если бы конструктор с 1 кортежем был I 'explicitдолжен быть явным, в случае если шаблон создается сsizeof...(Args) == 1, Я'Я не сильно против LWG 2051, но я думаю, что исправлять его изолированно было бы неудачно, так как яЯ бы предпочел более общее решение, которое также исправляет пример в LWG 2051.
 Andy Prowl19 февр. 2013 г., 17:30
@JonathanWakely: с другой стороны, если у вас есть функция, принимающая кортеж (foo(tuple)), Я хотел бы иметь возможность назвать это так:.foo({3, 3.0, "3"})
 PlasmaHH19 февр. 2013 г., 17:08
Мы должны написать петицию в комитет, чтобы сделать ее неявной для всех областей> 1 ...
 Jonathan Wakely19 февр. 2013 г., 17:28
Что касается неявной конструкции, я неэто не нужнонет причин предпочитатьtuple a = { ... }; надtuple a{...} форма. Единый init не делаетне требует=и если тип не являетсят подвижный тыне может использовать=, так что'Лучше прекратить использовать его и жить в 2011+
 gnzlbg19 февр. 2013 г., 17:22
@JonathanWakely, почему конструктор был помечен как явный?
 Andy Prowl19 февр. 2013 г., 17:28
@JonathanWakely: яя не беруpair В качестве примера хорошего дизайна яЯ скорее задаюсь вопросом, почему существует такое расхождение междуpair и структура данных, которая должна быть обобщениемpair, Я согласен, что конструктор должен быть явным для 1-кортежей.
 Andy Prowl19 февр. 2013 г., 17:12
@PlasmaHH: Действительно, я действительно нене понимаю, почему это помечено как.explicit
 Andy Prowl19 февр. 2013 г., 19:23
@Yakk: Ваш интересный аргумент, но я думаю, что программисту шаблона variadic придется принять это во внимание: они должны знать, что их функция может работать с кортежами любой арности, поэтому они должны явно ее конструировать или полагаться на механизм Джонатан Уэйкли предлагает. С другой стороны, яЯ все еще хотел бы иметь возможность неявно создавать кортеж для передачи его в качестве аргумента. Для меня две вещи не являются взаимоисключающими: конструктор может быть сделан явныма также мы могли бы иметь общий способ указать, что мы "используя явный конструктор
 Andy Prowl19 февр. 2013 г., 17:23
@JonathanWakely: Вы предложили обобщенное решение, которое действительно интересно, но применимо только к возвращаемым значениям. Почему бы не позволитьтакже неявная конструкция кортежей? Я не вижу причин, по которым при инициализации копированияtuples и работа для spair
 Andy Prowl19 февр. 2013 г., 17:07
@gnzlbg: Да, это не должно компилироваться.
 Jonathan Wakely19 февр. 2013 г., 17:26
pair это не образец хорошего дизайна, с которым нужно сравнивать все вещи, так что я нет купить "это работает для пары, так что это должно работать для кортежа " как достаточно хороший аргумент :) Конструктор был помечен как явный, потому что вы хотите, чтобы он был явным для случая с 1 кортежем. AFAIK никто не думал делать 1-кортежи особым случаем и иметь неявный ctor для других кортежей, пока он не был поздно.

Энди Проул дает правильный ответ. Я хотел прокомментировать реализацию libc ++, и формат комментариев не позволяет мне достаточно места или вариантов форматирования.

Даниэль КрюОколо года назад у нас с Глером был разговор на эту тему, и он убедил меня, что этот вопрос заслуживает того, чтобы добавить расширение в libc ++, чтобы получить опыт работы с ним. И до сих пор отзывы были положительными. Однако я хочу прояснить: это не так просто, как удалениеexplicit из ctor.explicit constexpr tuple(UTypes&&...)

Daniel»План состоит в том, чтобы датьtuple конструктор, который полностью уважает каждый элементнеявная / явная конструкция. А такжеесли каждый элемент будет построен неявно из каждого аргумента в списке инициализатора,затем конструкция кортежа неявная, иначе она будет явной.

Например:

Дано:

#include 

struct A
{
};

struct B
{
    B() = default;
    B(A);
};

struct C
{
    C() = default;
    explicit C(A);
};

Затем:

std::tuple
test0()
{
    return {};  // ok
}

Ничего особенного не скажешь об этом. Но и это нормально:

std::tuple
test1B()
{
    return {A()};  // ok B(A) implicit
}

потому что преобразование изA вB неявно. Однако следующее является ошибкой во время компиляции:

std::tuple
test1C()
{
    return {A()};  // error, C(A) is explicit
}

потому что преобразование изA вC явный Эта логика продолжается для многоэлементных кортежей. Чтобы происходило неявное преобразование, каждый элемент должен иметь неявное преобразование из списка аргументов:

std::tuple
test2B()
{
    return {A(), A()};  // ok each element has implicit ctor
}

std::tuple
test2C()
{
    return {A(), A()};  // error, C(A) is explicit
}

Я должен подчеркнуть: это расширение libc ++ в настоящее время.

Обновить

Чико сделал хорошее предложение, чтобы я обновил этот ответ:

Поскольку этот ответ был дан, Даниэль КрюГлер написалбумага и представил его комитету C ++ в Бристоле в апреле этого года. Несмотря на то, что документ был хорошо принят, он был рассмотрен слишком поздно, чтобы проголосовать за текущий рабочий проект.

Обновить

Daniel»Предложение теперь является частьютекущий рабочий проект, Реализация libc ++ теперь станет стандартом в этом отношении для следующего стандарта C ++ (мы надеемся, C ++ 17).

 pepper_chico24 мая 2013 г., 00:43
Я думаю это'Стоит упомянутьпредложение в ответ.
 Howard Hinnant22 февр. 2013 г., 20:51
Не в это время. Однако обратите внимание, что попытка портирования - это ошибка времени компиляции, за которой следует тривиальное исправление.
 Nikolai19 авг. 2013 г., 12:28
And if every element will construct implicitly from every argument in the initializer list, then the tuple construction is implicit, else it will be explicit. Не могли бы вы объяснить, почему это необходимо? Если какой-либо элемент не может быть создан неявно, это будет ошибкой компиляции, будь то явный конструктор кортежа или нет.
 bames5322 февр. 2013 г., 20:22
Есть ли способ отключить такие расширения в libc ++, чтобы сохранить переносимость кода?
 bames5322 февр. 2013 г., 22:17
Поддерживаете ли вы список расширений libc ++? Я не'Я ничего не вижу в быстром просмотре исходного кода libc ++, и я понимаю, что вы довольно много экспериментируете. --- Это'Верно, что однократное портирование является тривиальным, но если кто-то непрерывно интегрируется со сборочными ботами на полдюжины платформ, это 's удобнее иметь вещи на вашей рабочей платформе. Не то чтобы яЯ утверждаю, что это заслуживает особого внимания; если бы это было действительно важно, libc ++ с открытым исходным кодом, и я мог бы работать над этим сам ...
 Howard Hinnant22 февр. 2013 г., 22:57
Нет я нет, и этоХорошее предложение. Хотя с головы я могуне думаю о другом, кроме этого. Ой,allocator::propagate_on_container_move_assignment являетсяtrue_type, Иногда я просто раздражаюсь. ;-) Хотя этот уже вошел в WP для следующего стандарта:cplusplus.github.com/LWG/lwg-defects.html#2103

Ваш ответ на вопрос