अलग-अलग सी ++ कंपाइलर के बीच ऑटो प्रकार की बेमेल कटौती


10

इसलिए, मैं आधुनिक सी ++ के कुछ स्वाद में डॉट उत्पाद ( https://en.wikipedia.org/wiki/Dot_product ) को लागू करने की कोशिश कर रहा हूं और निम्नलिखित कोड के साथ आया:

#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/onbuildocs/gcc-9.2.0/gcc/C_002b_002b-Extensions.html#C003b_002b-Extensions में जी) के बाद से मेरी जानकारी के अनुसार ऑटो घोषणा सूची में विभिन्न प्रकारों को सही ढंग से संभालता है,
  • या किसी भी संयोग से g ++ ने दो प्रकारों को अलग करने के लिए कटौती नहीं की (... hm ...)
  • या कुछ और?

इस लंबे प्रश्न के माध्यम से पढ़ने के लिए धन्यवाद। (एक बोनस के रूप में अगर कोई जवाब दे सकता है कि क्यों महान होगा iccपर विफल रहता है static_assert।)


1
यहाँ क्या उपयोग std::forward<Args>(args)है?
Evg

test.cpp: फ़ंक्शन में 'int main ()': test.cpp: 4: 5: error: 'ऑटो' के लिए असंगत कटौती: 'लॉन्ग इंट' और फिर 'डबल' 4 | ऑटो आई = 0 एल, एफ = 0.0; | ^ ~~~ g ++ के साथ, इसलिए ऐसा लगता है कि यह सामान्य रूप से इसका विस्तार नहीं करता है।
n314159

मुद्रण प्रकार हमें देता है: std :: initializer_list <int>, int const * std :: initializer_list <int>, int const * g ++ में, इसलिए यह विभिन्न प्रकारों को घटाता है।
n314159

3
जीसीसी संकलित नहीं करता है 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*

-std=c++17 -pedantic -Wall -Wextraबिना किसी चेतावनी या त्रुटि के झंडे के साथ gcc संस्करण 9.2.0 का उपयोग करना ।

मानक की आपकी टिप्पणी से यह कार्यक्रम बीमार होता है और मानक निर्दिष्ट करता है कि एक नैदानिक ​​संदेश (चेतावनी या त्रुटि) का उत्सर्जन किया जाना चाहिए जब तक कि अन्यथा निर्दिष्ट न किया जाए (जो कि इस मामले में नहीं है)। इसलिए मैं कहूंगा कि यह gcc में एक बग है।

यह एक ज्ञात बग है


चूंकि यह एक बहुत ही सुविधाजनक बग है ... कुछ लोग इसे एक विशेषता होने का तर्क दे सकते हैं: आपकी अंतर्दृष्टि के लिए धन्यवाद!
फेरेंक डेक

यह बहुत अच्छा होगा अगर कोई g++इस बारे में बग दर्ज कर सकता है ।
अंडरस्कोर_ड

1
मैंने पहले कभी ऐसा नहीं किया है लेकिन मैं कुछ घंटों में इस पर गौर कर सकता हूं।
n314159

gcc.gnu.org/bugzilla/show_bug.cgi?id=92509 आशा है कि यह एक सनसनीखेज बग रिपोर्ट है।
n314159

0

static_assertअसफल आईसीसी पर निश्चित रूप से एक बग है। मुझे 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;
    };
}

क्या इसके लिए आईसीसी के खिलाफ बग है? :-)
अंडरस्कोर_ड

आपने कहा कि आईसीसी में स्पष्ट रूप से एक बग है, इसलिए मुझे आश्चर्य है कि अगर उनके पास पहले से ही किसी के द्वारा प्रस्तुत इस बग की रिपोर्ट है। यदि नहीं, तो इसे बनाने के लिए एक अच्छा समय हो सकता है।
अंडरस्कोर_ड

1
@underscore_d, मैंने अभी तक जाँच नहीं की है, लेकिन मैं करूँगा।
Evg
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.