3

関数のパラメーターの型の解釈と作成に関して、C++ が C よりも「リラックスした」方法で動作する理由を理解するのに苦労しています。

C は世界で最も単純なことを行います。それはあなたが書いたものに固執し、それだけです。一方、C++ は私が本当に理解できないねじれた方法で動作します。

たとえば、関数に渡されたときに人気argvがあり、その理由が本当にわかりません.char* []char**char * const *

C と C++ のこの違いについて説明しているこの記事を PDFで読むこともできます。この記事も次のフレーズで終わります。

C++ は、関数シグネチャを決定するときにパラメーター宣言のトップレベルの cv 修飾子を無視しますが、それらの cv 修飾子を完全に無視するわけではありません。

また、この号をオンラインで見つけることができないので ( Embedded System Programming - February 2000 、およびこの古い号は無料です )、このフレーズが何を意味するのか疑問に思っています。

この動作が C++ での方法である理由を誰かが説明できますか?

編集:

私の例の1つは

#include <stdio.h>

void foo(int argc, const char *const *const argv) {
  printf("%d %s\n", argc, argv[0]);
}

int main(int argc, char *argv[]) {
  foo(argc, argv);
  return (0);
}

これをコンパイルするとgcc 4.8.1、予想されるエラーが発生します

gcc cv_1.c 
cv_1.c: In function ‘main’:
cv_1.c:8:3: warning: passing argument 2 of ‘foo’ from incompatible pointer type [enabled by default]
   foo(argc, argv);
   ^
cv_1.c:3:6: note: expected ‘const char * const* const’ but argument is of type ‘char **’
 void foo(int argc, const char *const *const argv) {
      ^

argvこの出力は、次のように解釈される事実を暗黙的にします。char**

4

5 に答える 5

6

関数の引数は、値または参照によって渡すことができます。参照による場合、最上位の修飾子がないため、そのケースは無視できます。

値によるパラメーターの場合、最上位の修飾子はコピーのみに影響し、その引数のコピー構築に使用される元の修飾子からは完全に独立しています。最上位の修飾子が署名から削除されていない場合、次の 2 つの関数は有効であり、異なるオーバーロードになります。

void f(int       i);
void f(int const i);

問題は、f(1)2 つのオーバーロードのどちらを選択する必要があるかということです。ここでの問題は、引数が const であるかどうかは、それを構築できるものに影響しないため、コンパイラはどちらが正しいオーバーロードであるかを決して解決できないことです。解決策は簡単です。署名では、最上位の修飾子が削除され、両方が同じ関数になります。

于 2013-12-03T19:16:06.200 に答える
4

これは推測ですが、その理由は、修飾子を持つ関数パラメーターは引数のコピーに過ぎないからです。検討:

void foo(int * const a, volatile int b) { … }

これらの修飾子が言うことは、関数定義のコードはa(const であるため)変更されずb、C++ 実装では未知の方法で の値にアクセスできるということです。(これはかなり奇妙です。揮発性オブジェクトは、通常、ハードウェア レジスタや、おそらくプロセス間で共有されるデータのようなものです。しかし、問題をデバッグしているとしましょう。そのため、一時的にb揮発性をマークして、デバッガーでアクセスできるようにします。)

C++ 実装は、 を定義するコードをコンパイルおよび実行する際にa、これらの修飾子を尊重する必要があるため、これらの修飾子を無視することはできません。bfoo

ただし、 への呼び出し元のビューを考慮してfooください。が const またはvolatile としてfoo扱われるという事実は、呼び出し元には関係ありません。指定された引数はすべて (たとえば、レジ​​スタまたはスタックに) コピーされ、 に渡されます。値を渡すだけでした。の宣言に修飾子がない場合:abfoofoo

void foo(int *a, int b) { … }

その場合、呼び出し元の動作は変わりません。いずれにせよ、単に引数の値を渡し、 を呼び出しますfoo。したがって、これらの 2 つの宣言はfoo、呼び出し側から見ると同一であり、同じ署名を持ちます。

于 2013-12-03T19:16:02.990 に答える
3
void foo( char const * const * const) {}
void bar( char *x[]) {
  foo(x); // warning in C, nothing in C++
}

この例を C としてコンパイルすると警告が生成されるが、C++ では診断が生成されない理由は、C と C++ がchar *[]異なる型として扱われているからでも、別の場所で破棄または挿入constされているからでもなく、単に C と C++ が ' を定義しているからです。互換性のあるポインター型は異なります。C の厳密な規則は実際のエラーを防止していないため、C++ は規則を緩和します。

char const * const * const考えてみてください: で行うのが合法ではないで正確に何ができるでしょうchar **か? 変更を加えることができないため、エラーを導入することはできず、そのような制限はほとんど価値がありません。

constただし、これは、 s の挿入がエラーを生成する可能性のあるコードを許可しないと言っているわけではありません。例えば:

void foo(char const **c) { *c = "hello"; }

void bar(char **c) {
  foo(c);
  **c = 'J';
}

上記のコードは、許可されていれば文字列定数に書き込みますが、これは違法です。

C++ は、C よりも安全なプログラムを許可するために C から規則を緩和しながら、上記が許可されないように互換性のないポインター型を慎重に定義します。

C のルールの利点の 1 つは、ルールが非常に単純であることです。基本的:

2 つのポインター型に互換性を持たせるには、両方が同じように修飾され、両方が互換性のある型へのポインターでなければなりません。

任意の修飾子 q について、q 修飾されていない型へのポインターは、その型の q 修飾されたバージョンへのポインターに変換される場合があります。元のポインターと変換されたポインターに格納されている値は、比較すると等しくなります。

一方、C++ の規則はいくつかの段落に渡って続き、許可されるポインター変換を正確に指定するために複雑な定義を使用します。うさぎの穴は C++11 4.4 [conv.qual] パラグラフ 4 から始まります。


この言葉はいったい何を意味するのだろうか。

彼は、関数が定義されているときにパラメーターが const として宣言されている場合、コンパイラーは関数定義がパラメーターに対して非 const 操作を実行することを許可しないという事実に言及している可能性が最も高いです。

void foo(int x);
void bar(int x);

void foo(int const x) {
  ++x; // error, parameter is const
}

void bar(int x) {
  ++x; // okay, parameter is modifiable.
}
于 2013-12-03T19:25:20.387 に答える
1

コメントするには観測が大きすぎます。

C の警告が表示constされるのは、最初の だけです。 C++ (Visual) は、8 つのうち 2 つについて不平を言います。理由がわかりませんか?char const * const * const x

私見:どちらの言語も3番目に区別されずconst、呼び出し関数の観点からは余分に見えます。

void fooccc( char const * const * const x) { if(x) return; }
void foocc_( char const * const *       x) { if(x) return; }
void fooc_c( char const *       * const x) { if(x) return; }
void fooc__( char const *       *       x) { if(x) return; }
void foo_cc( char       * const * const x) { if(x) return; }
void foo_c_( char       * const *       x) { if(x) return; }
void foo__c( char       *       * const x) { if(x) return; }
void foo___( char       *       *       x) { if(x) return; }

int g(char *x[]) {
  fooccc(x); // warning in C passing argument 1 of 'fooccc' from incompatible pointer type
  foocc_(x); // warning in C "
  fooc_c(x); // warning in C "   error in C++ cannot convert parameter 1 from 'char *[]' to 'const char **const ' Conversion loses qualifiers
  fooc__(x); // warning in C "   error in C++ cannot convert parameter 1 from 'char *[]' to 'const char **'       Conversion loses qualifiers
  foo_cc(x); // no problem in C  no problem in C++
  foo_c_(x); // no problem in C  no problem in C++
  foo__c(x); // no problem in C  no problem in C++
  foo___(x); // no problem in C  no problem in C++
  return 0;
  }

注: Eclipse、gcc -std=c99 -O0 -g3 -Wall
C++ Visual Studio 10.0

于 2013-12-04T18:44:34.833 に答える