3

ポインタを介してプロシージャを呼び出すことは可能ですか?インターネット上では何も見つかりませんでしたが、次の実験的なコードは警告なしにコンパイルされます。

#include <iostream>
#include <ctime>

using namespace std;

void PrintCurrentClock()
{
    cout<<clock()<<endl;
}

void PrintCurrentTimeStamp()
{
    cout<<time(0)<<endl;
}


int main()
{
    void* pF = PrintCurrentClock;

    pF;

    pF = PrintCurrentTimeStamp;

    pF;

    system("Pause");
    return 0;
}

*pFがいくらか「透明」であるかのように、出力は空です。

4

4 に答える 4

5

CとC++の両方に、探していることを実行できる関数ポインターがあります。

void (*pf)(void);
pf = FunctionWithVeryVeryVeryLongNameThatPrintsTheCurrentClockStateUsingStdCoutOutputStream;
pf();

括弧内はvoidオプションです。

このトピックで何も見つからなかった理由は、歴史的な理由から、Cの関数とプロシージャの両方が関数と呼ばれるためです(理由はvoid、元の言語にはプロシージャがなく、intデフォルトでプロシージャが返され、戻り値が無視されたためです)。C ++は、この命名規則を継承しました。

于 2012-08-09T14:37:11.603 に答える
4

必要なのは関数ポインタです:

void (*pF)() = PrintCurrentClock;
pF();

(一部の人々は、書くこと&PrintCurrentClockはより良いスタイルであると主張します)

ご覧のとおり、関数ポインターの構文は非常に扱いにくく(特に「奇妙な」引数を持つ関数ポインターを使用し始めた場合)、一部のコンパイラー最適化が正しく機能しなくなる可能性があるため、通常は実際に必要な場合にのみ使用されます(たとえば、コールバックの場合ですが、C ++では関数がよく使用されます)。


期待どおりに機能しないのに、なぜコードがコンパイルされるのですか?の

void* pF = PrintCurrentClock;

PrintCurrentClockは、void (*pF)()暗黙的にvoid *1に変換されます。その後、書く

pF;

式を評価し、pFその戻り値を破棄します。これは事実上何もしません(5;関数呼び出しを含まない式を記述した場合とまったく同じです)。


  1. 実際には、C ++標準では関数ポインタからへの暗黙的な変換が提供されていないため、この変換は自動的には行われないはずvoid *です。これをg++4.6で正しくコンパイルすると、エラーが発生します。

    matteo@teolapmint ~/cpp $ g++ -Wall -Wextra -ansi -pedantic testfuncptr.cpp 
    testfuncptr.cpp: In function ‘int main()’:
    testfuncptr.cpp:19:20: error: invalid conversion from ‘void (*)()’ to ‘void*’ [-fpermissive]
    testfuncptr.cpp:21:15: warning: statement has no effect [-Wunused-value]
    testfuncptr.cpp:23:22: error: invalid conversion from ‘void (*)()’ to ‘void*’ [-fpermissive]
    testfuncptr.cpp:25:23: warning: statement has no effect [-Wunused-value]
    testfuncptr.cpp:27:39: error: ‘system’ was not declared in this scope
    

    これは、それらの変換が許可されていないこと、pF;指示が操作されていないこと、および忘れていることを示しています#include <cstdlib>system("pause");とにかく移植性はありませんが)。

于 2012-08-09T14:37:28.270 に答える
2

関数ポインタを作成して使用する方法は少しずれています。これを行う方法の例を次に示します。

void proc() {
    cout << "Hello from proc" << endl;
}

..。

void (*pproc)() = proc;

pproc();
于 2012-08-09T14:36:32.453 に答える
1

はい、できます:

型システムは少し複雑ですが。
したがって、通常、typedefで関数へのポインタをラップします。

typedef <returnType> (*<TypeName>)(<ParameterList>);

// In your case:

tpyedef void (*PtrToTimeFunc)();

// Now your pointer types look like normal variables:
PtrToTimeFunc  pf = &PrintCurrentTimeStamp;

// Calling them is like normal:
pf(); // If it needed parameters then put them as normal.

C ++コンパイラは、コードを最適化するために関数ポインタを渡すことができないためです。C ++では、ファンクターを使用するのが一般的です。ファンクターは関数のように動作するオブジェクトですが、オブジェクトであるため、状態を含めることもできます(他の言語のクロージャのように)。

struct MyFunctor
{
    // To make a functor just override the operator ()
    // You can make it return any type and take any parameters (just like a function).
    int operator()() const
    {
        return time(NULL);
    }

    // Add any state and constructors etc. you like here.
    // Though note: because I declared the operator() as const you
    // can not mutate the state via the function call (remove cost)
    // if you want to do that.
};

// declaring a functor just like any-other object.
MyFunctor  myFunctor;

// calling. Just like a function.
myFunctor();

わかった。では、なぜこれがポインタよりも役立つのでしょうか。

標準アルゴリズムで使用する場合。のタイプでアルゴリズムを定義するとfunctor、コンパイラはアルゴリズムコードを生成し、ファンクタのすべてのコードも利用できるようになります(過去に最適化できない関数ポインタとは異なります)。これにより、コンパイラーは完全な最適化セットを適切に実行できます。

std::generate(cont.begin(), cont.end(), myFunctor);

そのため、C++11ではラムダを導入しました。これらは基本的に、その場で定義できる関数です。ただし、ラムダはコンパイラによって生成されたファンクタと考える方が簡単です(can定義の一部として現在の状態をキャプチャするため)。

std::generate(cont.begin(), cont.end(), [](){return time(NULL);});

// [] - defines the state that is being captured.
//      Think of this like the constructor capturing objects.
//      In this case take no state.
//
// () - parameter list
//      In this case no parameters
//
// {} - The code.

より興味深い例:

std::vector<int> data; // fill vector
std::for_each(data.begin(), data.end(), [](int& value){value += 4;}); // Add 4 to each member.
于 2012-08-09T15:45:24.287 に答える