【问题标题】:Mismatched deduction of auto types between different c++ compilers不同c++编译器之间自动类型推导不匹配
【发布时间】:2020-03-10 08:15:00
【问题描述】:

所以,我正在尝试以某种现代 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&lt;int&gt;' in declaration of 'v1' and deduced as 'const int *' in declaration of 'i1',所以我想听听您的意见:

感谢您阅读这个冗长的问题。 (作为奖励,如果有人能回答为什么 iccstatic_assert 上失败,那就太好了。)

【问题讨论】:

  • 这里std::forward&lt;Args&gt;(args)有什么用?
  • test.cpp:在函数“int main()”中:test.cpp:4:5:错误:“auto”的推导不一致:“long int”,然后是“double” 4 |自动 i = 0l,f = 0.0; | ^~~~ 用g++,所以看起来一般不会扩展这个。
  • 打印类型给我们:std::initializer_list, int const* std::initializer_list, int const* in g++,所以它推导出不同的类型。
  • GCC does not compile 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
  • @underscore_d 我想是的。最小的例子是template &lt;typename T&gt; void f(T a) { auto v = {a}, i = v.begin(); },当被调用时,例如f(1);。重写为void f(int a) { /* same body */ } 会导致编译错误。

标签: c++ gcc clang auto type-deduction


【解决方案1】:

从我的 cmets 扩展:

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;

如果我们编译您的程序并打印变量的类型 (with this method),我们会得到以下输出:

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,没有任何警告或错误。

根据您对标准的评论,该程序格式错误,标准specifies 表示除非另有说明(在这种情况下并非如此),否则应该发出诊断消息(警告或错误)。因此我会说这是 gcc 中的一个错误。

a known bug

【讨论】:

  • 因为它是一个非常方便的错误......有些人可能会认为它是一个功能:D 感谢您的见解!
  • 如果有人能针对 g++ 提交关于此问题的错误,那就太好了。
  • 我以前从未这样做过,但我可以在几个小时内查看它。
  • gcc.gnu.org/bugzilla/show_bug.cgi?id=92509 希望这是一个明智的错误报告。
【解决方案2】:

ICC 上的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;
    };
}

【讨论】:

  • 是否存在针对 ICC 的错误? :-)
  • 你说ICC明显有bug,不知道他们是否已经有人提交了这个bug的报告。如果没有,这可能是创建一个的好时机。
  • @underscore_d,我还没有检查,但我会的。
猜你喜欢
  • 2023-01-28
  • 1970-01-01
  • 1970-01-01
  • 2016-12-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多