【C++言語入門】 第25回 クラス中のラムダ式と引数


関数の引数に関数を
qsort、C++言語ではstd::sortなどでしょうか?
関数を引数に代入しよう
戻り値の型 関数名(関数の戻り値の型 (*引数名)(引数...), ...) { 処理 };
関数の戻り値の型 (*引数名)(引数...)の部分が引数なのか...
#include <iostream>
int Add(int a, int b) {
int c = a + b;
return c;
}
void Message(int (*add)(int, int), const char* pmsg, int a, int b) {
std::cout << pmsg << add(a, b) << std::endl;
return;
}
int main(int argc, char* argv[]) {
Message(Add, "一般の関数の足し算 ", 1, 2);
Message([](int x, int y) -> int { return x + y; }, "ラムダ式の足し算 ", 3, 4);
return 0;
}

Message関数の引数としてAdd関数を割り当てているのか?
intで、int型の引数を2つ持っている関数や ラムダ式 であればOKということか
キャプチャリストを持つラムダ式を渡す
Message関数の引数に キャプチャリスト を持つ ラムダ式 を渡してみましょう
#include <iostream>
void Message(int (*add)(), const char* pmsg) {
std::cout << pmsg << add() << std::endl;
return;
}
int main(int argc, char* argv[]) {
int x = 1;
int y = 2;
Message([=]() -> int { return x + y; }, "ラムダ式の足し算 ");
return 0;
}

std::function<>を使ってみますわ
戻り値の型 関数名(std::function<関数の戻り値の型(引数...)> 引数名, ...) { 処理 };
std::function<関数の戻り値の型(引数...)> 引数名の部分が、そのまま引数となるのだ
#include <iostream>
#include <functional>
void Message(std::function<int()> add, const char* pmsg) {
std::cout << pmsg << add() << std::endl;
return;
}
int main(int argc, char* argv[]) {
int x = 1;
int y = 2;
Message([=]() -> int { return x + y; }, "ラムダ式の足し算 ");
return 0;
}

#include <functional>は忘れずに記述して下さいね
std::function<>であれば、 ラムダ式 だけではなく、通常の関数を割り当てることもできますわ
引数の型にauto
std::function<>を使用できるなら、autoによる 型の推論 も可能ではないのか?
autoによる 型の推論 はできないようですわ
autoにすることはできるようですが...
関数の戻り値に関数?
関数の引数に関数や ラムダ式 を使えるのであれば、戻り値にも関数や ラムダ式 を使えると考えるのは当然です
できます
関数の戻り値に、変数や引数と同様の型指定を行えばOKです
例えばこんな感じです
#include <iostream>
#include <functional>
std::function<int(int, int)> SelectFunc(int s) {
int x = 2;
std::function<int(int, int)> f;
if (s == 0) {
f = [](int a, int b) -> int { return a + b; }; // (1)
} else {
f = [=](int a, int b) -> int { return (a + b) * x; }; // (2)
}
return f;
}
int main(int argc, char* argv[]) {
auto func0 = SelectFunc(0);
auto func1 = SelectFunc(1);
std::cout << func0(1, 2) << std::endl;
std::cout << func1(1, 2) << std::endl;
return 0;
}

SelectFuncは、指定した引数により、ただの足し算の ラムダ式 (1)か、足した後に2倍する ラムダ式 (2)を返します
返された関数はメイン関数内で実行されます
さて、ここで問題になるのが(2)の キャプチャリスト です
上記の例では コピー "="を指定しているため、SelectFunc関数を抜けて"x"が破棄されても問題ありません
ここで(2)の キャプチャリスト を参照"&"にしてみましょう
#include <iostream>
#include <functional>
std::function<int(int, int)> SelectFunc(int s) {
int x = 2;
std::function<int(int, int)> f;
if (s == 0) {
f = [](int a, int b) -> int { return a + b; }; // (1)
} else {
f = [&](int a, int b) -> int { return (a + b) * x; }; // (2)
}
return f;
}
int main(int argc, char* argv[]) {
auto func0 = SelectFunc(0);
auto func1 = SelectFunc(1);
std::cout << func0(1, 2) << std::endl;
std::cout << func1(1, 2) << std::endl;
return 0;
}

メイン関数でfunc1を実行する時点でSelectFunc関数を抜けているので、参照している"x"は既に破棄されています
結果として、意図した値とは異なる結果が表示されます
まぁ、こういった落とし穴があるので、関数や ラムダ式 を返す関数は、かなり慎重に作る必要があります
と云うか、経験上、関数や ラムダ式 を返す関数の需要は殆どありません
もしも、関数や ラムダ式 を返す関数が必要になった場合は、アルゴリズムを考え直した方が良いです
クラスの中のラムダ式
クラス中のラムダ式からthisを通してアクセス
thisは、 キャプチャリスト に記載することで ラムダ式 内で使うことができるようになりますわ
thisを通して メンバ変数 や 他のメソッド にアクセスすることができますわ
public:以外でアクセス指定されていても問題なくアクセスできますわ
Messageメソッドを持ったCircleクラスで、Areaメソッドを ラムダ式 に書き換えてみましょう
#pragma once
#ifndef CIRCLE_H
#define CIRCLE_H
#include <iostream>
const double kPI = 3.14159265358979323846;
const int kSize = 50;
/// @brief 円
class Circle {
double diameter_; // 直径
char* pmessage_;
public:
Circle(double diameter) : diameter_(diameter), pmessage_(nullptr) {
pmessage_ = new char[kSize];
}
virtual ~Circle() {
delete[] pmessage_;
pmessage_ = nullptr;
}
double Diameter() const { return diameter_; }
void Diameter(double diameter) { diameter_ = diameter; }
virtual const char* Message() const noexcept {
auto area = [this]() {
double radius = this->Diameter() / 2;
double a = radius * radius * kPI;
return a;
};
sprintf_s(pmessage_, kSize, "円の面積は%fです。", area());
return pmessage_;
}
};
#endif // CIRCLE_H
#include <iostream>
#include "circle.h"
int main(int argc, char* argv[]) {
Circle c(10.0);
std::cout << c.Message() << std::endl;
return 0;
}

this->Diameter()の部分でthisを通してDiameterメソッドを呼び出していますわ
this->diameter_として、直接 メンバ変数 にアクセスすることもできますわ
this->を省略することも可能ですわ
thisを通して メンバ変数 や 他のメソッド にアクセスすることができますわ
auto area = [=]() {
double radius = this->Diameter() / 2;
double a = radius * radius * kPI;
return a;
};
クラス中のラムダ式からthisを通して代入
thisを通して ラムダ式 の中から メンバ変数 の値を変更できるのか?
Messageメソッドにconst修飾子が付加されているため、そのままでは メンバ変数 を変更できませんわ
const修飾子がなければ問題なく変更できるということか
Messageメソッドからconst修飾子を外して、 ラムダ式 内から"diameter_"を変更してみましょう
#pragma once
#ifndef CIRCLE_H
#define CIRCLE_H
#include <iostream>
const double kPI = 3.14159265358979323846;
const int kSize = 50;
/// @brief 円
class Circle {
double diameter_; // 直径
char* pmessage_;
public:
Circle(double diameter) : diameter_(diameter), pmessage_(nullptr) {
pmessage_ = new char[kSize];
}
virtual ~Circle() {
delete[] pmessage_;
pmessage_ = nullptr;
}
double Diameter() const { return diameter_; }
void Diameter(double diameter) { diameter_ = diameter; }
virtual const char* Message() noexcept {
auto area = [this]() {
this->Diameter(5.0);
double radius = this->Diameter() / 2;
double a = radius * radius * kPI;
return a;
};
sprintf_s(pmessage_, kSize, "円の面積は%fです。", area());
return pmessage_;
}
};
#endif // CIRCLE_H

this->Diameter(5.0);により、直径を半分にしていますわ
メンバ変数にラムダ式
ところで、通常の変数に ラムダ式 を割り当てられるのであれば、 メンバ変数 にも ラムダ式 を割り当てることができるのではないでしょうか
とりあえず確認してみましょう
#pragma once
#ifndef CIRCLE_H
#define CIRCLE_H
#include <iostream>
#include <functional>
const double kPI = 3.14159265358979323846;
const int kSize = 50;
/// @brief 円
class Circle {
double diameter_; // 直径
char* pmessage_;
std::function<double()> area_ = [this]() {
double radius = this->Diameter() / 2;
double a = radius * radius * kPI;
return a;
};
public:
Circle(double diameter) : diameter_(diameter), pmessage_(nullptr) {
pmessage_ = new char[kSize];
}
virtual ~Circle() {
delete[] pmessage_;
pmessage_ = nullptr;
}
double Diameter() const { return diameter_; }
void Diameter(double diameter) { diameter_ = diameter; }
virtual const char* Message() noexcept {
sprintf_s(pmessage_, kSize, "円の面積は%fです。", area_());
return pmessage_;
}
};
#endif // CIRCLE_H

メンバ変数 area_に ラムダ式 を指定しています
型はautoを使うことができないので、std::function<>を使っています
なお、 キャプチャリスト にはthis以外は デフォルトコピーキャプチャ "="や デフォルト参照キャプチャ "&"以外は使うことができないようです
使用時にメソッドから変数などから値を渡したい場合には、引数を使用します
とはいえ、メソッドでも概ね同じことが可能です
無理をして メンバ変数 に ラムダ式 を割り当てる必要はありません
Discussion