Невідповідне виведення авто типів між різними компіляторами c ++


10

Отже, я намагаюся реалізувати крапковий продукт ( https://en.wikipedia.org/wiki/Dot_product ) в якомусь ароматі сучасного C ++ і придумав наступний код:

#include <iostream>

template<class... Args>
auto dot(Args... args)
{
    auto a = [args...](Args...)
    { 
        return [=](auto... brgs)
        {
            static_assert(sizeof...(args) == sizeof...(brgs));

            auto v1 = {args...}, i1 = v1.begin();
            auto v2 = {brgs...}, i2 = v2.begin();
            typename std::common_type<Args...>::type s = 0;

            while( i1 != v1.end() && i2!= v2.end())
            {
                s += *i1++ * *i2++;
            } 
            return s;
        };
    };
  return a(std::forward<Args>(args)...);
}

int main()
{
    auto a = dot(1,3,-5)(4,-2,-1);
    std::cout << a << std::endl;
}

Онлайн: https://gcc.godbolt.org/z/kDSney, а також: cppinsights

Код, описаний вище, добре компілює та виконує g++, однак clang( iccі msvc) задавлюється на ньому:

clang++ ./funcpp.cpp --std=c++17                                                                                                                                                                                                                                                        
./funcpp.cpp:12:4: error: 'auto' deduced as 'std::initializer_list<int>' in declaration of 
        'v1' and deduced as 'const int *' in declaration of 'i1'
                        auto v1 = {args...}, i1 = v1.begin();
                        ^         ~~~~~~~~~       ~~~~~~~~~~
./funcpp.cpp:28:11: note: in instantiation of function template specialization 
        'dot<int, int, int>' requested here
        auto a = dot(1,3,-5)(4,-2,-1);
                 ^
1 error generated.

Тепер, якщо я розбити визначення v1, v2, i1, i2як:

auto v1 = {args...} ;
auto i1 = v1.begin();
auto v2 = {brgs...};
auto i2 = v2.begin();

clangі msvcжодних проблем не виникає, iccвсе-таки задихається:

<source>(10): error: static assertion failed

                static_assert(sizeof...(args) == sizeof...(brgs));

                ^

          detected during instantiation of "auto dot(Args...) [with Args=<int, int, int>]" at line 30

compilation aborted for <source> (code 2)

Execution build compiler returned: 2

Однак якщо я видаляю кривдників static_assertтодіicc також не виникає проблем із складанням коду.

І поруч із (типовим) питанням: що правильно і чому :) конкретне питання:

Відповідно до [dcl.spec.auto]:

якщо тип, який замінює тип заповнення місця, не є однаковим у кожному вирахуванні, програма неправильно формується

clangправильно встановлено, що у відповідному рядку визначено два різні типи: 'auto' deduced as 'std::initializer_list<int>' in declaration of 'v1' and deduced as 'const int *' in declaration of 'i1'тож я хотів би почути вашу думку:

  • чи потрапив я в незадокументоване розширення g ++ з огляду на цю конкретну ситуацію (не згадується в https://gcc.gnu.org/onlinedocs/gcc-9.2.0/gcc/C_002b_002b-Extensions.html#C_002b_002b-Extensions ) з моменту g ++, наскільки мені відомо правильно обробляє різні типи в списку автоматичних декларацій,
  • або випадково g ++ не визначив, що два типи відрізняються (... hm ...)
  • чи щось інше?

Дякуємо, що прочитали це довге запитання. (Як бонус, якщо хтось міг би відповісти, чому iccне вдається, static_assertбуло б чудово.)


1
У чому тут користь std::forward<Args>(args)?
Evg

test.cpp: У функції 'int main ()': test.cpp: 4: 5: помилка: непослідовний відлік для 'auto': 'long int', а потім 'подвійний' 4 | auto i = 0l, f = 0.0; | ^ ~~~ З g ++, тому, здається, це взагалі не поширюється.
n314159

друк типів дає нам: std :: inicijalizer_list <int>, int const * std :: inicijalizer_list <int>, int const * в g ++, тому він виводить різні типи.
n314159

3
GCC не складається auto v = { 1, 2, 3 }, i = v.begin(); . Не розумійте, що вона складається з тієї ж інседіальної лямбда. Мінімальний приклад: gcc.godbolt.org/z/a5XyxU . Він навіть компілюється всередині визначеного користувачем функтора: gcc.godbolt.org/z/eYutyK , або функції шаблону: gcc.godbolt.org/z/jnEYXh .
Даніель Лангр

2
@underscore_d Я гадаю, що так. Дуже мінімальний приклад template <typename T> void f(T a) { auto v = {a}, i = v.begin(); }, коли викликається, наприклад, як f(1);. Переписано як void f(int a) { /* same body */ }причини помилки компіляції.
Даніель Лангр

Відповіді:


2

Розширення з моїх коментарів:

g ++ робить це не завжди, розглянемо приклад auto i = 0l, f = 0.0;, він дає помилку:

test.cpp: In function int main()’:
test.cpp:4:5: error: inconsistent deduction for auto’: long int and then double
    4 |     auto i = 0l, f = 0.0;

Якщо ми складемо вашу програму і надрукуємо типи змінних (за допомогою цього методу ), отримаємо такий вихід:

v1: std::initializer_list<int>, i1: int const*
v2: std::initializer_list<int>, i2: int const*

з використанням gcc версії 9.2.0, з прапорами -std=c++17 -pedantic -Wall -Wextraбез будь-якого попередження або помилки.

За вашим коментарем до стандарту ця програма неправильно сформована і стандарт уточнює що слід випускати діагностичне повідомлення (попередження або помилка), якщо не вказано інше (чого це не в цьому випадку). Отже, я б сказав, що це помилка в gcc.

Це відомий клоп .


Оскільки це дуже зручна помилка ... дехто може стверджувати, що це особливість: D Дякую за вашу думку!
Ferenc Deak

Було б чудово, якби хтось міг подати помилку з g++цього приводу.
підкреслюй_

1
Я ніколи цього не робив, але за кілька годин я можу заглянути.
n314159

gcc.gnu.org/bugzilla/show_bug.cgi?id=92509 Сподіваюся, що це розумний звіт про помилку.
n314159

0

The static_assertНевдача на ICC, безумовно, помилка. Я знайшов просте вирішення, перейшовши static_assertна окрему функцію. Не дуже елегантне рішення, але воно працює.

З незначними модифікаціями, це код, який компілюється з GCC, Clang та ICC:

template<std::size_t size, class... Args>
void args_no_guard(Args... args)
{
    static_assert(sizeof...(args) == size);
}

template<class... Args>
auto dot(Args... args)
{
    return [=](auto... brgs)
    {
        constexpr auto n = sizeof...(args);
        args_no_guard<n>(brgs...);

        using T = std::common_type_t<decltype(args)..., decltype(brgs)...>;
        const T v1[]{static_cast<T>(args)...};
        const T v2[]{static_cast<T>(brgs)...};

        T dot = 0;
        for (std::size_t i = 0; i < n; ++i)
            dot += v1[i] * v2[i];
        return dot;
    };
}

Чи є помилка в ICC для цього? :-)
підкреслюй_

Ви сказали, що в ICC явно є помилка, тож мені цікаво, чи вони вже мали хтось повідомлення про цю помилку. Якщо ні, то це може бути вдалий час для його створення.
підкреслюй_

1
@underscore_d, я ще не перевірив, але буду.
Evg
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.