std :: пара <авто, авто> тип повернення


16

Я грав autoу std::pair. У наведеному нижче коді функція fповинна повертати а std::pairтипів, які залежать від параметра шаблону.

Робочий приклад:

ПРИКЛАД 1

template <unsigned S>
auto f()
{
    if constexpr (S == 1)
        return std::pair{1, 2}; // pair of ints
    else if constexpr (S == 2)
        return std::pair{1.0, 2.0}; // pair of doubles
    else
        return std::pair{0.0f, 0.0f}; // pair of floats
}

Це працює з gcc 9.2, gcc 10.0, clang 9.0 та clang 10.0.

Далі, я хотів би чітко записати тип повернення як std::pairчіткість:

ПРИКЛАД 2

template <unsigned S>
std::pair<auto, auto> f()
{
    if constexpr (S == 1)
        return {1, 2};
    /* ... */
}

Як gcc 9.2 / 10.0, так і clang 9.0 / 10.0 не вдалося скомпілювати це.

gcc 9.2

error: invalid use of 'auto'
error: template argument 1 is invalid // first argument (auto) of std::pair
error: template argument 2 is invalid // second argument (auto) of std::pair
error: cannot convert '<brace-enclosed initializer list>' to 'int' in return

З останнього повідомлення про помилку, gcc 9.2, здається, вважає, що std::pair<auto, auto>це int. Як це можна пояснити?

gcc 10,0

error: returning initializer list

Ця помилка зрозуміла, однак я очікував, що конструктор std::pairбуде викликаний, чи щось тут мені не вистачає?

кланг 9.0 і 10.0

'auto' not allowed in template argument
excess elements in scalar initializer
no matching function for call to 'f'

Гаразд, Кланг нічого з цього не любить. З другого повідомлення про помилку, схоже, що Кланг також вважає тип повернення int.

Нарешті, щоб виправити помилку, отриману при компілюванні з gcc 10.0, я вирішив повернути std::pairявно:

ПРИКЛАД 3

template <unsigned S>
std::pair<auto, auto> f()
{
    if constexpr (S == 1)
        return std::pair{1, 2};
    /* ... */
}

кланг 9.0 і 10.0

Те саме, що раніше, але з додатковим:

no viable conversion from returned value of type 'std::pair<int, int>' to function return type 'int'

Тут Кланг все ще думає, що ми повертаємось int?

gcc 9.2

Те саме, що і раніше.

gcc 10,0

Це працює!

Я припускаю, що деякі функції все ж таки мають бути реалізовані, або в одній із ситуацій, описаних вище, чи є компілятор, який правильний, а інший неправильний? На мою думку, приклад 2 повинен працювати. Або не слід?

Відповіді:


23

Синтаксис:

std::pair<auto, auto> f() { return std::pair(1, 2); }
~~~~~~~~~~~~~~~~~~~~~

Був частиною оригінальної програми TS Concepts, але не був включений до пропозиції щодо концепцій, що входить до C ++ 20. Таким чином, єдиними типами заповнювачів у C ++ 20 є auto(та їх різновиди, як auto**) decltype(auto), та обмежені заповнювачі ( Concept autoта їх варіації). Цей тип вкладеного заповнювача буде дуже корисним, але не є частиною C ++ 20, тому декларація функції неправильно формується.

Тепер gcc дозволяє це, оскільки gcc реалізував TS Concepts, і я думаю, що вони вирішили зберегти цю функцію. Кланг ніколи не реалізовував TS, так що це не так.

Так чи інакше, це:

std::pair<auto, auto> f() { return {1, 2}; }

Завжди буде погано сформований. Сенс синтаксису полягає в тому, що ми виводимо тип повернення, а потім вимагаємо, щоб він відповідав pair<T, U>деяким типам Tі U. Ми в основному намагаємося викликати придуману функцію:

template <typename T, typename U>
void __f(std::pair<T, U>);

__f({1, 2}); // this must succeed

Але ви не можете вивести тип із {1, 2}списку - у скопленому списком не існує типу. Можливо, це те, що слід вивчити (як це зрозуміти принаймні у простому випадку, як це), але це ніколи не було дозволено. Тож відкидання це правильно в будь-якому випадку.

Нарешті:

gcc 9.2, здається, вважає, що std::pair<auto, auto>це int. Як це можна пояснити?

Чомусь (можливо, це пов'язано з нашою спадщиною C із неявним int), коли gcc не розпізнає і не розуміє тип, він просто використовується intяк заповнювач у повідомленнях про помилки. Це надзвичайно заплутано, адже, очевидно, що придумали gcc, intа не вихідний код. Але так воно і є.


"Braced-init-list не має аргументу типу" мені незрозумілий. std :: пара <int, int> f () {return {1,2}; } працює, і {1,2} не має типу (він викликає конструктор std :: pair <int, int>, наскільки я це розумію). Можливо, за допомогою <auto, auto> компілятор не може вивести типи 1 і 2 у списку ініціалізаторів {1, 2}?
mfnx

@mfnx Не має аргументу типу , просто не має типу. обмежені списки init можуть використовуватися лише в певних ситуаціях - наприклад, ініціалізація відомого типу. Але їх не можна використовувати у відрахуванні - тому що вони не мають типу. За винятком auto x = {1, 2};робіт, але тільки якщо всі типи однакові.
Баррі

2
Більшість компіляторів замість того, щоб просто зупинятися на першій помилці, намагаються відновити її, щоб вони могли повідомити про додаткові помилки. Це, як правило, означає припустити, що все нерозбірливе - це int. Це не те, що intє заповнювачем у повідомленнях про помилки; компілятор дійсно вважає, що це int. (Щоб зробити це зрозумілішим, gcc, мабуть, сказав би в якийсь момент "припускаючи інт".)
Raymond Chen

2
Зауважте, що ще одним можливим способом розширення було б дозволити виведення аргументу шаблону класу для типів повернення, оскільки std::pair __f{1,2};працює.
Девіс Оселедець

2
@DavisHerring Я не дуже хотів би std::optional f() { return 4; }працювати.
Баррі
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.