15

次の (単純化された) コードを複数のプラットフォーム用にコンパイルしようとすると、IBM の xlC_r. さらに調査したところ、comeau と clang でも失敗することがわかりました。g++ および Solaris の CC で正常にコンパイルされます。

コードは次のとおりです。

int main()
{
    int a1[1];
    bool a2[1];

    for (int *it = a1, *end = a1+1; it != end; ++it) {
        //...
        bool *jt = a2, *end = a2+1;
        //...
    }
}

xlC_r エラー:

"main.cpp", line 8.25: 1540-0400 (S) "end" has a conflicting declaration.
"main.cpp", line 6.25: 1540-0425 (I) "end" is defined on line 6 of "main.cpp".

クランエラー:

main.cpp:8:25: error: redefinition of 'end' with a different type
        bool *jt = a2, *end = a2+1;
                        ^
main.cpp:6:25: note: previous definition is here
    for (int *it = a1, *end = a1+1; it != end; ++it) {
                        ^

コモーエラー:

"ComeauTest.c", line 8: error: "end", declared in for-loop initialization, may not
          be redeclared in this scope
          bool *jt = a2, *end = a2+1;
                          ^

問題は、なぜこれがエラーになるのかということです。

2003年の標準に目を通すと、次のように書かれています(6.5.3):

The for statement
    for ( for-init-statement; condition; expression ) statement
is equivalent to
    {
        for-init-statement;
        while ( condition ) {
            statement;
            expression;
        }
    }
except that names declared in the for-init-statement are in the same
declarative-region as those declared in condition

ここでは、条件で宣言された名前はありません。

さらに、それは言います(6.5.1):

When the condition of a while statement is a declaration, the scope
of the variable that is declared extends from its point of declaration
(3.3.1) to the end of the while statement. A while statement of the form
    while (T t = x) statement
is equivalent to
    label:
    {
        T t = x;
        if (t) {
            statement;
            goto label;
        }
    }

繰り返しますが、条件に宣言がないため、これが関連しているかどうかはわかりません。したがって、6.5.3 から同等の書き直しを行うと、私のコードは次のようになります。

int main()
{
    int a1[1];
    bool a2[1];

    {
        int *it = a1, *end = a1+1;
        while (it != end) {
            //...
            bool *jt = a2, *end = a2+1;
            //...
            ++it;
        }
    }
}

これにより、明らかに end を再宣言できます。

4

5 に答える 5

8

基準はややあいまいです。ループと同等であるとして引用したコードはwhile、ループ内の宣言が条件内の宣言を隠すことができる内部スコープがあることを意味します。ただし、標準には次のようにも記載されています(C++03が手元にないため、C++11を引用します):

6.4/2 条件の規則は、選択ステートメントと and ステートメントの両方に適用さforwhileます

6.4/3 条件によって制御されるサブステートメントの最も外側のブロックで名前が再宣言されている場合、名前を再宣言する宣言は形式が正しくありません。

6.5.3/1 for-init-statement で宣言された名前は、条件で宣言されたものと同じ declarative-region にあります

それらの間では、名前を再宣言できないことを意味します。

古い (1998 年より前の) バージョンの言語では、for-init-statement 内の宣言がループの外側の宣言領域に配置されていました。これは、コードが有効であることを意味しますが、これはそうではありません。

for (int i = ...; ...; ...) {...}
for (int i = ...; ...; ...) {...}  // error: redeclaration of i
于 2012-09-10T12:17:50.550 に答える
3

コードは正しいと思います。IMO、問題はブレースにあります。for ステートメントは次のように定義されていることに注意してください。

for ( for-init-statement; 条件; 式 ) ステートメント

ループ本体には中かっこがなく、複合ステートメントを使用するときに追加されます。ただし、複合ステートメントは独自の宣言領域を追加するため、内部宣言がfor-init-statement.

次のコードは、clang と G++ で問題なくコンパイルされます (二重中かっこに注意してください)。

for (int *it = a1, *end = a1+1; it != end; ++it) {{
    //...
    bool *jt = a2, *end = a2+1;
    //...
}}

私の推測では、clang コンパイラは、ループが次のように定義されているかのように最適化しようとします。

for ( for-init-statement; 条件; 式 ) { statement-seq }

意味の微妙な変化により、両方の宣言領域が融合されます。

ただし、2番目に、ブレースがまったく使用されていない場合でも、次のようになります。

for (int x=0; ;)
    char x;

正しくコンパイルされるはずです。C++ ドラフト 6.5、par. 2:

iteration-statement のサブステートメントは、暗黙的にブロック スコープを定義します。

したがって、char x;自体がブロック スコープを (暗黙的に) 定義し、競合する宣言が発生することはありません。

于 2012-09-10T12:29:39.757 に答える
0

標準の現在のバージョンは、これについて明確です。

6.5 繰り返しステートメント [stmt.iter]

2 -反復ステートメント(ループなど) のサブステートメントforは、ループを介して毎回出入りするブロックスコープ (3.3) を暗黙的に定義します。

C にも同様の規則があります。

6.8.5 反復ステートメント

セマンティクス

5 - 反復ステートメントは、そのスコープが、それを囲むブロックのスコープの厳密なサブセットであるブロックです。ループ本体は、スコープが反復ステートメントのスコープの厳密なサブセットであるブロックでもあります。

于 2012-09-10T12:38:52.130 に答える
0

一般に古いコンパイラの中には、 for ループで宣言された変数をループのスコープ外で見えるようにするものがあります。

すべてのコンパイラを新しい (より良い) 方法で動作させるには、次のようにマクロを宣言します。

// In older compilers, variables declared in a for loop statement
// are in the scope of the code level right outside the for loop.
// Newer compilers very sensibly limit the scope to inside the
// loop only. For compilers which don't do this, we can spoof it
// with this macro:
#ifdef FOR_LOOP_VARS_NEED_LOCAL_SCOPE
   #define for if(0); else for
#endif

次に、古い動作を持つ各コンパイラに対して、FOR_LOOP_VARS_NEED_LOCAL_SCOPE を定義します。たとえば、MSVC < 8 の場合は次のようになります。

#ifdef _MSC_VER
   #if _MSC_VER < 1400   //  earlier than MSVC8
      #define FOR_LOOP_VARS_NEED_LOCAL_SCOPE
   #endif
#endif
于 2012-09-10T16:17:36.810 に答える
0

ここでのパーティーには少し遅れましたが、これは C++11 標準の次の箇所で最も明確に許可されていないと思います。

3.3.3 ブロックスコープ [basic.scope.local]

4 - for-init-statement、for-range-declaration、および if、while、for、および switch ステートメントの条件で宣言された名前は、if、while、for、または switch ステートメントに対してローカルです (制御されたステートメントを含む)そのステートメントの後続の条件でも、制御されたステートメントの最も外側のブロック (または、if ステートメントの場合は、最も外側のブロックのいずれか) でも再宣言してはなりません。6.4 を参照してください。

于 2015-12-31T00:19:29.480 に答える