การหักประเภทอัตโนมัติไม่ตรงกันระหว่างคอมไพเลอร์ c ++ ที่ต่างกัน


10

ดังนั้นฉันกำลังพยายามที่จะใช้ผลิตภัณฑ์ dot ( 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' และ 'double' 4 | อัตโนมัติ i = 0l, f = 0.0; | ^ ~~~ ด้วย g ++ ดังนั้นดูเหมือนว่ามันจะไม่ขยายโดยทั่วไป
n314159

การพิมพ์ประเภทให้เรา: std :: initializer_list <int>, int const * std :: initializer_list <int>, int const * ใน g ++ ดังนั้นมันจึงอนุมานประเภทที่แตกต่างกัน
n314159

3
GCC ไม่ได้รวบรวม auto v = { 1, 2, 3 }, i = v.begin();ไม่เข้าใจว่ามันรวบรวมแลมบ์ดา insiede เดียวกัน ตัวอย่างน้อยที่สุด: gcc.godbolt.org/z/a5XyxU มันยังรวบรวมภายในที่ผู้ใช้กำหนด functor: gcc.godbolt.org/z/eYutyKหรือฟังก์ชั่นแม่แบบ: gcc.godbolt.org/z/jnEYXh
Daniel Langr

2
@underscore_d ฉันคิดเช่นนั้น ตัวอย่างที่น้อยมากเมื่อเรียกเช่นเป็นtemplate <typename T> void f(T a) { auto v = {a}, i = v.begin(); } f(1);เขียนซ้ำเป็นvoid f(int a) { /* same body */ }สาเหตุของข้อผิดพลาดในการรวบรวม
Daniel Langr

คำตอบ:


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++เกี่ยวกับเรื่องนี้
underscore_d

1
ฉันไม่เคยทำแบบนั้นมาก่อน แต่ฉันสามารถดูได้ในไม่กี่ชั่วโมง
n314159

gcc.gnu.org/bugzilla/show_bug.cgi?id=92509หวังว่านี่เป็นรายงานข้อบกพร่องที่สมเหตุสมผล
n314159

0

ความ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 หรือไม่? :-)
underscore_d

คุณบอกว่ามีข้อบกพร่องอย่างชัดเจนใน ICC ดังนั้นฉันสงสัยว่าพวกเขามีรายงานข้อผิดพลาดนี้ที่ส่งโดยใครบางคน ถ้าไม่อย่างนี้อาจเป็นเวลาที่ดีในการสร้าง
underscore_d

1
@underscore_d ฉันยังไม่ได้ตรวจสอบ แต่ฉันจะทำ
Evg
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.