Подтвердить что ты не робот

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

Следующий код компилируется с 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, а также ответ Говарда Хиннанта ниже.

4b9b3361

Ответ 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).