C++関数の使用と呼び出し

4614 ワード

概要
本文は主に関数の定義と呼び出し方法を紹介し,関数の一致と実参加形参は紹介内ではない.
1つの関数には、関数名、パラメータ、戻り値の3つの基本要素が必要です.しかし、関数名がない特殊な関数があり、匿名関数と呼ばれています.
関数ベース
関数を呼び出すには、定義または宣言後にする必要があります.そうしないと、「未定義」エラーが表示されます.
auto add(const int x, const int y) -> int {
    return x + y;
}

int main(int, char **) {
    add(1, 2);
    return 0;
}

同様に,この関数の存在を先に宣言し,後で実現することもできる.
auto add(const int x, const int y) -> int;

int main(int, char **) {
    add(1, 2);
    return 0;
}

auto add(const int x, const int y) -> int {
    return x + y;
}

関数ポインタ
定義#テイギ#
ポインタは変数を指すだけでなく、関数を指すこともできます.他のポインタと同様に、関数ポインタは特定のタイプを指します.関数のタイプは、関数名に関係なく、その戻りタイプとパラメータタイプによって決まります.
上記で定義したadd関数を例にとると、addを指すポインタを宣言するには、関数名をポインタで置き換えるだけでよい.
int (*f) (const int x, const y);
f = add;

//   
int (*f) (const int x, const y) = add;

宣言を観察すると、(*f)は明らかにポインタであり、右側はそのパラメータテーブルであり、関数を指し、左側はその戻りタイプを示している.(*f)左右の括弧は少なくない.
よびだし
関数ポインタを持つと、通常の関数のように呼び出すことができます.
cout << f(1, 2) << endl;
// output: 3
fはポインタであるため、パラメータリストと戻り値が一致する限り、そのタイプに合致する他の関数を指すこともできます.
auto add(const int x, const int y) -> int {
    return x + y;
}

int (*f) (const int x, const int y);
f = add;

cout << f(1, 2) << endl;
// output: 3

f = minus;
cout << f(1, 2) << endl;
// output: -1

では、関数ポインタを普通のポインタのように、パラメータを通じて別の関数に渡すことができますか?もちろんいいです.addを拡張し、CPSスタイルのadd_cpsを実現することができます.
auto add_cps(const int x, const int y, int k (int)) -> int {
    auto a = add(x, y);
    return k(a);
}

int (*f) (int) = inc;
cout << add_cps(1, 2, f) << endl;
kは、int (*k) (int)の書き方と等価な後続計算の関数ポインタである.
関数オブジェクト
定義#テイギ#
C++11の後、STLにはヘッダが設けられ、ここでは多くの関数オブジェクトを処理することができる.std::functionを利用して、関数ポインタと同じ効果を達成することができます.
std::function f = add;
cout << f(1, 2) << endl;

ポインタfを関数オブジェクト(funtion objects[1])に変換し、fのタイプを観察すると、std::functionは関数オブジェクトであることを示し、intは関数の戻り値であり、括弧内は関数のパラメータリストであり、関数ポインタと比較して、それらの表現情報はほとんど変わらない.
バイアス関数
さっき使ったinc関数を覚えていますか?これはadd(1)のバイアス関数と言えるが,std::bindを用いてadd関数を特例化することができる.
std::function inc = std::bind(add, 1, std::placeholders::_1);

cout << inc(1) << endl;
// output: 2
std::placeholders::_1はプレースホルダであり、最初のパラメータがこの位置に埋め込まれることを示し、同様に_2および他のプレースホルダもある.
関数オブジェクトを渡す
同様に、関数オブジェクトもパラメータとして使用できます.元のadd_cpsパラメータリストの関数ポインタを関数オブジェクトに変更します.
auto add_cps(const int x, const int y, std::function k) -> int {
    auto a = add(x, y);
    return k(a);
}

cout << add_cps(1, 2, inc) << endl;
// output: 4

改造後,関数ポインタと同様の効果を達成した.
高次関数
私たちは関数を伝達することができて、それでは私たちももっと高いレベルの関数を実現することができて、例えばHaskellの中のmapfilterなどです.
auto map(std::function f, const vector &in) -> vector {
    vector v;
    for (const auto &i : in) {
        v.push_back(f(i));
    }

    return v;
}

auto filter(std::function f, const vector &in) -> vector {
    vector v;
    for (const auto &i : in) {
        if (f(i)) {
            v.push_back(i);
        }
    }

    return v;
}

注意:mapはC++でデータ構造を表し、ここではデモのみを行う.
Lambda関数
Lambda関数(または匿名関数)は4つの書き方を提供している[2].いくつかの簡略化された計算では、addincなどの具名関数を再定義する以外に、ある場所で一時的に新しい関数を使用することもでき、このときlambdaに渡すことができます.
上ではmap関数を実現し、各数に(+ 1)を受け入れたいと考えています.mapは関数を受け入れています.ここではlambdaを一時的に使用することができます.
std::vector v{1, 2, 3};
map([](auto i) { return i + 1; }, v);
// 2 3 4
[](auto i) { return i + 1; }はlambda関数であり、匿名関数をstd::functionに割り当てて、関数オブジェクトに再変更することもできます.
std::function inc = [](auto i) {
    return i + 1;
};

showVec(map(inc, v));

lambdaと関数オブジェクトがあれば、いつものように外部定義で一度だけ使用した関数を定義する必要はありません.読み取り可能な上で、lambdaとfunctionを組み合わせると直感的になるかもしれないと思います.
  • http://en.cppreference.com/w/cpp/utility/functional ↩
  • http://en.cppreference.com/w/cpp/language/lambda ↩