45218


c++中級編の参考書の下記のプログラムコメントになっている部分の挙動がわからないです。

[code]

    reinterpret_cast<int(*)(const void *, const void*)>(int_cmp)
    
[/code]

キャストしているのはかわかるのですが`int(*)`というのと`(const void*,const void*)`  
が何をしているのかがわからないです、ポインタがどうたらっていうキャストをしているのはなんとなく察しがつくのですがしっかり理解したいので解説お願いします。

[code]

    int int_cmp(const int* a,const int* b){
        if (*a < *b) {
            return -1;
        }
        else if(*a > *b) {
            return 1;
        }else {
            return 0;
        }
    }
    
    int main() {
    
        int i = 0;
        int nx = 0;
        int no = 0;
        cout << "配列の要素数:";
        cin >> nx;
    
        int *x = new int[nx];
        cout << nx << "個の要素数を昇順に入力せよ、\n";
    
        for (i = 0; i < nx; i++) {
            cout << "x[" << i << "]:";
            cin >> x[i];        
        }
    
        cout << "探索する値:";
        cin >> no;
    
        int *p = reinterpret_cast<int*>(
            bsearch(&no, x, nx, sizeof(int),
            /*ここです。*/
            reinterpret_cast<int(*)(const void *, const void*)>(int_cmp))
        );
            /* */
    
        if (p != NULL) {
            cout << "x[" << (p - x) << "]が一致します。\n";
        }
        else {
            cout << "見つかりません\n";
        }
    
        _getch();
        return 0;
    }
    
[/code]


CC BY-SA 4.0
2018-07-01T14:19:38.050
45217
2018-07-06T05:43:58.777
2018-07-01T16:41:05.607
3060
question
c++
キャストで理解できない場所　関数ポインタ


**`int(*)(const void *, const void*)`について**

`int(*)`と`(const void *, const
void*)`と分けてしまうと、わかりにくくなってしまうので、まとめて考えます。まず`(*)`は後で考えることにして、`F`と置き換えてみます。すると元の式は

`int F(const void *, const void*)`

になります。こうしてみると、この式は全体として関数の宣言の形になっており、実際、 **`const void
*`の引数を二つ取り、`int`を返す関数**を意味しています。ここで`F`は`(*)`であり、このアスタリスクはポインタを示していることを併せて考えれば、全体として`int(*)(const
void *, const void*)`は、

**`const void *`の引数を二つ取り、`int`を返す関数へのポインタ**

ということになります。

* * *

**キャストについて**

関数 bsearch の宣言がないので、cstdlib で宣言されている標準関数の bsearch だと仮定して説明します。

bsearch の最後の引数の型は`int (*)(const void*,const void*)`ですが、その一方、渡したい関数 int_cmp
の型は`int int_cmp(const int* a,const int*
b)`です。関数の型は、戻り値の型と引数の型を合わせたものなので、引数の型が合わず、直接、関数のポインタを渡そうとしてもコンパイルエラーになってしまいます。それでキャストが必要になります。

`reinterpret_cast<int(*)(const void *, const void*)>(int_cmp)`

の意味は、 **`const int *`の引数を二つ取り、`int`を返す関数へのポインタ`int_cmp` を`const void
*`の引数を二つ取り、`int`を返す関数へのポインタ**に変換せよ、ということです。関数から関数へのキャストは、C++の仕様で決まったルールはないので、`reinterpret_cast`で強引に変換する必要があります。


CC BY-SA 4.0
2018-07-01T15:05:20.010
45218
2018-07-01T15:05:20.010
3605
45217
answer


コメント欄にて質問が更新されているのでそっちに対応

> なぜconst void* なのでしょうか？引数をvoidにしてししまうと値を渡せないんと思うんですけど

比較関数の引数は `void` ではなくて `void*` ですよ（事の本質に関係ないので `const`
は省略）この２つはまったく違います。結局のところわかっていないのはここなのでは？

[c](/questions/tagged/c "'c' のタグが付いた質問を表示")/[c++](/questions/tagged/c%2b%2b
"'c++' のタグが付いた質問を表示") において `void*` とはポインタの総称型とでも呼ぶべきものです。  
- 任意の型のオブジェクトへのポインタ値を `void*` に格納できる  
- `void*` の値から、元の型の値に戻すことができる  
ことが言語仕様で保証されています。 (JIS X 3014:2003 4.10-2, 5.2.9-10)

`bsearch` や `lfind` や `qsort` は任意の型に対して処理ができるようにわざと `void*` を使う設計となっています。

下記では、短くしたい（この回答で書きたい重要部分でない）ためオーバーフローするとダメなコードをわざと提示しています。実用的なコードにするにはリンク先参照のこと。  
ref.
[C言語のPOSIX定義関数のlfindで配列要素の検索がうまくできているか自信がない](https://ja.stackoverflow.com/questions/45278/)

`int` の比較を行う関数なら

[code]

    int int_comparator(const void* lp, const void* rp) {
       int lval=*(const int*)lp; // void* → int* へ戻すことができる
       int rval=*(const int*)rp;
       return lval-rval;
    }
    
[/code]

のように `void*` から `int*` に戻すキャストを書けばよいのです。

`mytype` の比較を行う関数なら

[code]

    int mytype_comparator(const void* lp, const void* rp) {
       const mytype* l=(const mytype*)lp; // void* → mytype* へ戻すことができる
       const mytype* r=(const mytype*)rp;
       return l->foo-r->foo; // mytype にメンバ foo があるものとする
    }
    
[/code]

上記 `int_comparator` や `mytype_comparator` なら `bsearch`
にキャストなしに直接渡すことができ、かつ、動くことが保証されています。逆に、提示の `int_cmp` は `bsearch`
が要求している関数の型と違うので `int_cmp`
へのポインタをキャストして無理やり渡しても動作保証はありません（ほとんどの場合には問題ないですけど）。関数ポインタの型表記は難しくて読みづらいのですが、それについては既に
Hideki さんから回答がありますよね。

さてここで話題になっている `bsearch` や `lfind` は `qsort` は [c++](/questions/tagged/c%2b%2b
"'c++' のタグが付いた質問を表示")
が登場する前からある、とてつもなく古いものです。そのためキャストをわざわざ明示する必要があったり（ソースコードが見づらくなる）、関数ポインタ経由で比較を行ったり（遅い！）、慣れないと理解しがたい、使いにくい（現に今質疑応答の対象となってますよね）のです。

[c++](/questions/tagged/c%2b%2b "'c++' のタグが付いた質問を表示")
ならもっと良い（わかりやすくて高速動作するであろう）方法が用意されています。過去の遺物に手を出さずに、新しい良い方法を使ってみませんか？


CC BY-SA 4.0
2018-07-06T05:43:58.777
45354
2018-07-06T05:43:58.777
8589
45217
answer