Khấu trừ các loại tự động giữa các trình biên dịch c ++ khác nhau


10

Vì vậy, tôi đang cố gắng triển khai sản phẩm chấm ( https://en.wikipedia.org/wiki/Dot_product ) trong một số hương vị của C ++ hiện đại và đưa ra mã sau:

#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;
}

Trực tuyến: https://gcc.godbolt.org/z/kDSney và cả: cppinsights

Đoạn mã trên biên dịch và thực thi độc đáo với g++, tuy nhiên clang( iccmsvc) bị sặc trên nó:

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.

Bây giờ, nếu tôi phá vỡ các định nghĩa của v1, v2, i1, i2như:

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

clangmsvckhông có vấn đề gì, iccvẫn bị sặc:

<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

Tuy nhiên nếu tôi loại bỏ vi phạm static_assertthì icccũng không có vấn đề biên dịch mã.

Và bên cạnh câu hỏi (điển hình): câu nào đúng và tại sao :) câu hỏi cụ thể là:

Theo [dcl.spec.auto]:

nếu loại thay thế loại giữ chỗ không giống nhau trong mỗi lần khấu trừ, chương trình sẽ không được định dạng

clangxác định chính xác rằng có hai loại khác nhau được xác định trong dòng đang đề cập: 'auto' deduced as 'std::initializer_list<int>' in declaration of 'v1' and deduced as 'const int *' in declaration of 'i1'vì vậy tôi muốn nghe ý kiến ​​của bạn cho dù:

  • tôi đã nhấn một số tiện ích mở rộng g ++ không có giấy tờ khi xem xét tình huống cụ thể này (không được đề cập trong https://gcc.gnu.org/onlinesocs/gcc-9.2.0/gcc/C_002b_002b-Extensions.html#C_002b_002b-Extensions ) kể từ g ++ theo hiểu biết của tôi xử lý chính xác các loại khác nhau trong danh sách khai báo tự động,
  • hoặc bằng mọi cách, g ++ đã không suy ra hai loại là khác nhau (... hm ...)
  • hay cái gì khác?

Cảm ơn đã đọc qua câu hỏi dài này. (Như một phần thưởng nếu ai đó có thể trả lời tại sao iccthất bại trên static_assertsẽ rất tuyệt.)


1
Việc sử dụng std::forward<Args>(args)ở đây là gì?
Evg

test.cpp: Trong hàm 'int main ()': test.cpp: 4: 5: error: khấu trừ không nhất quán cho 'auto': 'long int' và sau đó 'double' 4 | tự động i = 0l, f = 0,0; | ^ ~~~ Với g ++, vì vậy có vẻ như nó không mở rộng điều này nói chung.
n314159

in các loại cho chúng ta: std :: initizer_list <int>, int const * std :: initizer_list <int>, int const * trong g ++, do đó nó suy ra các loại khác nhau.
n314159

3
GCC không biên dịch auto v = { 1, 2, 3 }, i = v.begin(); . Đừng hiểu rằng nó biên dịch cùng lambda insiede. Ví dụ tối thiểu: gcc.godbolt.org/z/a5XyxU . Nó thậm chí còn biên dịch bên trong một functor do người dùng định nghĩa: gcc.godbolt.org/z/eYutyK hoặc một hàm mẫu: gcc.godbolt.org/z/jnEYXh .
Daniel Langr

2
@underscore_d Tôi cho là vậy. Ví dụ rất tối thiểu là template <typename T> void f(T a) { auto v = {a}, i = v.begin(); }, khi được gọi, ví dụ, như f(1);. Viết lại là void f(int a) { /* same body */ }nguyên nhân lỗi biên dịch.
Daniel Langr

Câu trả lời:


2

Mở rộng từ ý kiến ​​của tôi:

g ++ không làm điều này luôn, xem xét ví dụ auto i = 0l, f = 0.0;, nó đưa ra lỗi:

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;

Nếu chúng tôi biên dịch chương trình của bạn và in các loại biến ( với phương thức này ), chúng tôi sẽ nhận được kết quả sau:

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

sử dụng gcc phiên bản 9.2.0, với cờ -std=c++17 -pedantic -Wall -Wextramà không có bất kỳ cảnh báo hoặc lỗi nào.

Theo nhận xét của bạn về tiêu chuẩn, chương trình này không đúng định dạng và tiêu chuẩn chỉ định rằng cần phải phát ra một thông báo chẩn đoán (cảnh báo hoặc lỗi) trừ khi có quy định khác (trong trường hợp này không phải là quy định khác). Do đó tôi sẽ nói rằng đây là một lỗi trong gcc.

Đây là một lỗi đã biết .


Vì đó là một lỗi rất tiện lợi ... một số người có thể cho rằng đó là một tính năng: D Cảm ơn những hiểu biết của bạn!
Ferenc Deak

Sẽ thật tuyệt nếu ai đó có thể đưa ra một lỗi chống lại g++điều này.
gạch dưới

1
Tôi chưa bao giờ làm điều đó trước đây nhưng tôi có thể xem xét nó trong một vài giờ.
n314159

gcc.gnu.org/ormszilla/show_orms.cgi?id=92509 Hy vọng đó là một báo cáo lỗi hợp lý.
n314159

0

Các static_assert thất bại trên ICC chắc chắn là một lỗi. Tôi tìm thấy một cách giải quyết đơn giản bằng cách chuyển static_assertsang một chức năng riêng biệt. Không phải là giải pháp rất thanh lịch, nhưng nó hoạt động.

Với những sửa đổi nhỏ, đây là mã biên dịch với GCC, Clang và 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;
    };
}

Có một lỗi chống lại ICC cho điều đó? :-)
gạch dưới

Bạn nói rõ ràng có một lỗi trong ICC, vì vậy tôi tự hỏi liệu họ đã có báo cáo về lỗi này do ai đó gửi chưa. Nếu không, đây có thể là thời điểm tốt để tạo một cái.
gạch dưới

1
@underscore_d, tôi chưa kiểm tra, nhưng tôi sẽ làm.
Evg
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.