不同C ++编译器之间的自动类型推导不匹配


10

因此,我试图以某种现代C ++的形式实现点积(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(和iccmsvc)阻塞了它:

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.

现在,如果我分手的定义v1v2i1i2,如:

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

clangmsvc没有问题,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'因此,我想听听您的意见是否:

感谢您阅读这个长问题。(作为奖励,如果有人可以回答为什么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 *在g ++中,std :: initializer_list <int>,int const *,因此可以推断出不同的类型。
n314159 '19

3
GCC 无法编译 auto v = { 1, 2, 3 }, i = v.begin();。不了解它会编译相同的insiede lambda。最小示例:gcc.godbolt.org/z/a5XyxU。它甚至可以在用户定义的函子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(); }当调用时,非常小的示例是as f(1);。重写为void f(int a) { /* same body */ }会导致编译错误。
Daniel Langr

Answers:


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_assertICC上的失败绝对是一个错误。通过移至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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.