【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