異なるC ++コンパイラ間での自動型の推測の不一致


10

だから、私はドット積を実装しようとしています( 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.

今、私はの定義を壊す場合はv1v2i1i2のように:

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問題の行で2つの異なるタイプが定義されていることを正しく識別しました。'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)ここの用途は何ですか?
平均

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 * in g ++が得られるため、さまざまなタイプが推測されます。
n314159

3
GCC はコンパイルしません auto v = { 1, 2, 3 }, i = v.begin();。同じinsiedeラムダをコンパイルすることを理解していません。最小限の例: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(); }、のように呼び出された場合、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*

-std=c++17 -pedantic -Wall -Wextra警告やエラーのないフラグ付きのgccバージョン9.2.0を使用します。

標準のコメントによって、このプログラムは形式が正しくなく、標準、特に指定されていない限り(この場合はそうではありません)、診断メッセージ(警告またはエラー)を出力するように指定しています。したがって、これは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、私はまだチェックしていませんが、チェックします。
平均
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.