C++におけるプログラミング技術は、年々進化を遂げており、その中でも型推論とconstexpr関数は、コードの効率性とパフォーマンスを向上させるための重要な要素です。型推論は、コンパイラが自動的に変数の型を決定する機能であり、開発者の手間を省き、コードの可読性を向上させます。一方、constexpr関数は、コンパイル時に評価される定数式を定義する機能であり、実行時のパフォーマンスを最適化します。本記事では、これらの技術の基本的な概念と、それらを効果的に組み合わせる方法について詳しく解説します。
C++の型推論とは
C++の型推論(type inference)は、コンパイラがコード中の変数や関数の型を自動的に決定する機能です。これにより、開発者は変数宣言時に明示的に型を指定する必要がなくなり、コードの簡潔さと可読性が向上します。
型推論の概要
型推論は、C++11で導入されたauto
キーワードを用いて実現されます。auto
を使用すると、コンパイラは変数の初期化式からその型を推論します。例えば:
auto x = 10; // xはint型と推論される
auto y = 3.14; // yはdouble型と推論される
auto s = "hello"; // sはconst char*型と推論される
型推論の利点
- 可読性の向上:型推論により、冗長な型宣言が不要になり、コードが簡潔になります。
- メンテナンス性の向上:型の変更が必要な場合、変更箇所が少なくて済むため、メンテナンスが容易になります。
- ジェネリックプログラミングの強化:テンプレートやラムダ式など、型を意識せずに汎用的なコードを書く際に便利です。
使用例
以下は、型推論を活用したコード例です:
std::vector<int> vec = {1, 2, 3, 4, 5};
for (auto it = vec.begin(); it != vec.end(); ++it) {
std::cout << *it << " ";
}
このように、auto
を使用することで、イテレータの型を明示する必要がなくなり、コードがより直感的になります。
constexpr関数の基本
constexpr関数は、コンパイル時に定数式を評価するための関数です。C++11で導入され、C++14およびC++17で機能が拡張されました。これにより、プログラムの実行時パフォーマンスが向上し、定数計算が可能になります。
constexpr関数の概要
constexpr関数は、関数の返り値がコンパイル時に決定される場合に使用されます。関数の定義には、constexpr
キーワードを使用します。例えば:
constexpr int square(int x) {
return x * x;
}
この関数は、コンパイル時に定数として評価されるため、実行時の計算コストが削減されます。
使用条件
- 引数と戻り値がリテラル型であること。
- 関数内で使用するすべての式がコンパイル時に評価可能であること。
C++14以降では、より柔軟な制約でconstexpr関数を使用できます。例えば、ローカル変数や複雑な制御構造を含む関数も定義可能です。
具体的な使用方法
以下は、constexpr関数の基本的な使用例です:
constexpr int factorial(int n) {
return (n <= 1) ? 1 : (n * factorial(n - 1));
}
int main() {
constexpr int result = factorial(5); // コンパイル時に計算される
std::cout << result << std::endl; // 出力: 120
}
この例では、factorial
関数をconstexprとして定義し、コンパイル時に計算させています。これにより、実行時に再計算する必要がなくなり、パフォーマンスが向上します。
利点
- パフォーマンス向上:実行時の計算が不要になり、プログラムのパフォーマンスが向上します。
- コードの安全性:コンパイル時に評価されるため、定数の誤りを早期に検出できます。
- 最適化:コンパイラによる最適化が可能になり、効率的なコードが生成されます。
constexpr関数を活用することで、C++プログラムの効率性と安全性を高めることができます。
型推論とconstexpr関数の組み合わせ
型推論とconstexpr関数を組み合わせることで、コードの効率性や可読性がさらに向上し、プログラムのパフォーマンスも最適化されます。このセクションでは、その具体的な利点と使用例を紹介します。
利点
- 簡潔で直感的なコード:型推論により、変数の型を自動的に推定できるため、コードが短くなり読みやすくなります。constexpr関数を併用することで、計算がコンパイル時に行われ、効率が上がります。
- パフォーマンスの向上:constexpr関数により、実行時の計算を避けられるため、プログラムの実行速度が向上します。また、型推論により適切な型が自動的に選択されるため、最適化が容易になります。
- メンテナンス性の向上:型推論により、コードの変更箇所が少なくなり、constexpr関数による定数計算の安全性が高まります。
具体例
以下の例では、型推論とconstexpr関数を組み合わせたコードを示します:
constexpr int computeSquare(int x) {
return x * x;
}
int main() {
auto result = computeSquare(5); // resultの型はintと推論される
constexpr auto compileTimeResult = computeSquare(10); // compileTimeResultはコンパイル時に計算される
std::cout << "Runtime result: " << result << std::endl; // 出力: Runtime result: 25
std::cout << "Compile-time result: " << compileTimeResult << std::endl; // 出力: Compile-time result: 100
}
この例では、computeSquare
関数がconstexprとして定義されており、コンパイル時と実行時の両方で使用されています。result
変数の型はauto
により自動的にint
と推論され、compileTimeResult
変数はコンパイル時に計算されます。
実用的な応用
型推論とconstexpr関数の組み合わせは、特に数値計算やグラフィックス処理など、計算コストの高い処理において効果を発揮します。以下は、その一例です:
constexpr double pi = 3.14159265358979323846;
constexpr double circleArea(double radius) {
return pi * radius * radius;
}
int main() {
auto radius = 5.0;
auto area = circleArea(radius); // areaの型はdoubleと推論される
std::cout << "Circle area: " << area << std::endl; // 出力: Circle area: 78.5398
}
この例では、円の面積を計算するcircleArea
関数がconstexprとして定義され、型推論により変数の型が自動的に決定されます。このように、型推論とconstexpr関数を組み合わせることで、効率的かつ可読性の高いコードを実現できます。
実用的な応用例
型推論とconstexpr関数を組み合わせた実用的な応用例をいくつか紹介します。これらの例は、現実のプロジェクトでどのようにこれらの技術を活用できるかを示します。
例1: 数学ライブラリの最適化
数学ライブラリでは、数値計算の効率性が非常に重要です。型推論とconstexpr関数を使用することで、計算のパフォーマンスを向上させることができます。
constexpr double pi = 3.14159265358979323846;
constexpr double circleCircumference(double radius) {
return 2 * pi * radius;
}
constexpr double circleArea(double radius) {
return pi * radius * radius;
}
int main() {
auto radius = 7.0;
auto circumference = circleCircumference(radius); // circumferenceの型はdoubleと推論される
auto area = circleArea(radius); // areaの型もdoubleと推論される
std::cout << "Circumference: " << circumference << std::endl; // 出力: Circumference: 43.9823
std::cout << "Area: " << area << std::endl; // 出力: Area: 153.938
}
この例では、円の周長と面積を計算する関数をconstexprとして定義し、型推論を使用して変数の型を自動的に推論しています。これにより、コードが簡潔で読みやすくなり、パフォーマンスも向上します。
例2: コンパイル時に決定される定数配列
コンパイル時に定数として評価される配列を定義することで、実行時のメモリ管理が最適化されます。
constexpr int fibonacci(int n) {
return (n <= 1) ? n : fibonacci(n - 1) + fibonacci(n - 2);
}
constexpr int fibonacciArray[10] = {
fibonacci(0), fibonacci(1), fibonacci(2), fibonacci(3), fibonacci(4),
fibonacci(5), fibonacci(6), fibonacci(7), fibonacci(8), fibonacci(9)
};
int main() {
for (auto value : fibonacciArray) {
std::cout << value << " "; // 出力: 0 1 1 2 3 5 8 13 21 34
}
std::cout << std::endl;
}
この例では、フィボナッチ数列を計算する関数をconstexprとして定義し、その結果を配列に格納しています。コンパイル時にすべての計算が行われるため、実行時のオーバーヘッドがありません。
例3: コンパイル時に最適化される設定値
設定値をコンパイル時に決定することで、プログラムの実行時に柔軟性と効率性を両立させることができます。
constexpr int maxBufferSize(bool highPerformanceMode) {
return highPerformanceMode ? 1024 : 256;
}
int main() {
constexpr auto bufferSize = maxBufferSize(true); // bufferSizeは1024と決定される
char buffer[bufferSize];
std::cout << "Buffer size: " << bufferSize << std::endl; // 出力: Buffer size: 1024
}
この例では、設定値をconstexpr関数で定義し、コンパイル時に決定することで、実行時のパフォーマンスを最適化しています。
これらの例から分かるように、型推論とconstexpr関数を組み合わせることで、C++プログラムの効率性、可読性、パフォーマンスを大幅に向上させることができます。実際のプロジェクトでこれらの技術を活用することで、より優れたソフトウェア開発が可能となります。
パフォーマンスへの影響
型推論とconstexpr関数の組み合わせは、プログラムのパフォーマンスに大きな影響を与えます。これらの機能を効果的に利用することで、実行速度の向上やリソースの効率的な使用が実現されます。このセクションでは、それぞれの技術がどのようにパフォーマンスに影響を与えるかを分析します。
型推論によるパフォーマンス向上
型推論は主にコードの可読性と保守性に寄与しますが、間接的にパフォーマンスにも影響を与えます。以下の点が重要です:
- コンパイラ最適化の強化:型推論を用いることで、コンパイラがコードの最適化を行いやすくなります。型が明確であるため、コンパイラはより適切な最適化手法を適用できます。
- コードの簡潔化:冗長な型宣言が不要になるため、コードが短くなり、エラーの発生を抑制します。これにより、デバッグやメンテナンスが容易になり、間接的に開発効率が向上します。
constexpr関数によるパフォーマンス向上
constexpr関数は、コンパイル時に計算を行うため、実行時の負荷を大幅に削減します。具体的には以下の利点があります:
- 実行時の計算コスト削減:コンパイル時に評価されるため、実行時に再計算する必要がありません。これにより、実行速度が向上します。
- コードの最適化:コンパイラは、constexpr関数の結果を定数として扱うため、最適化の余地が広がります。結果として、より効率的なマシンコードが生成されます。
- 定数の安全性:コンパイル時に評価されるため、定数の誤りが早期に検出され、バグの発生を防ぎます。
具体例によるパフォーマンス比較
以下に、constexpr関数を使用した場合と使用しない場合のパフォーマンスを比較する例を示します:
#include <iostream>
#include <chrono>
// 通常の関数
int factorial(int n) {
return (n <= 1) ? 1 : (n * factorial(n - 1));
}
// constexpr関数
constexpr int constexprFactorial(int n) {
return (n <= 1) ? 1 : (n * constexprFactorial(n - 1));
}
int main() {
auto start = std::chrono::high_resolution_clock::now();
int result = factorial(10); // 実行時に計算
auto end = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> duration = end - start;
std::cout << "Runtime factorial: " << result << ", Time taken: " << duration.count() << " seconds\n";
start = std::chrono::high_resolution_clock::now();
constexpr int constexprResult = constexprFactorial(10); // コンパイル時に計算
end = std::chrono::high_resolution_clock::now();
duration = end - start;
std::cout << "Constexpr factorial: " << constexprResult << ", Time taken: " << duration.count() << " seconds\n";
return 0;
}
このプログラムでは、通常の関数とconstexpr関数を比較しています。結果は、constexpr関数がコンパイル時に計算されるため、実行時の計算コストが大幅に削減されることを示しています。
まとめ
型推論とconstexpr関数は、それぞれ異なる方法でプログラムのパフォーマンスに寄与します。型推論はコードの簡潔化とコンパイラ最適化の強化を通じて、間接的にパフォーマンスを向上させます。一方、constexpr関数は、コンパイル時の計算により、直接的に実行時の負荷を削減します。これらの技術を適切に組み合わせることで、C++プログラムの効率性とパフォーマンスを大幅に向上させることができます。
注意点とベストプラクティス
型推論とconstexpr関数を効果的に活用するためには、いくつかの注意点とベストプラクティスを理解しておく必要があります。このセクションでは、これらの技術を使用する際の重要なポイントを紹介します。
型推論の注意点
1. 明示的な型指定の重要性
型推論は便利ですが、すべてのケースで使用するのが最適とは限りません。特に、複雑なコードや他の開発者が読む可能性のあるコードでは、明示的な型指定が可読性を向上させることがあります。
auto x = 42; // xの型はint
double y = 3.14; // 明示的にdoubleと指定
2. 一貫性の維持
コードベース全体で一貫したスタイルを維持することが重要です。型推論の使用は一貫して行い、場合によってはコメントで補足することが推奨されます。
auto it = container.begin(); // イテレータの型が自明な場合
constexpr関数の注意点
1. 制約に注意
constexpr関数は、コンパイル時に評価される必要があります。そのため、関数内で使用するすべての式がコンパイル時に評価可能であることを確認する必要があります。
constexpr int validConstexpr(int x) {
return x * x;
}
// 非constexprな関数には注意
int invalidConstexpr(int x) {
return rand() % x; // 実行時にしか評価できないためconstexprにできない
}
2. コンパイラのサポート
使用するコンパイラが、最新のC++規格に準拠していることを確認する必要があります。特に、C++14やC++17で導入された新しいconstexpr機能を使用する場合は、コンパイラのバージョンに依存します。
ベストプラクティス
1. 適切な用途での使用
型推論とconstexpr関数は、適切な状況で使用することで最大の効果を発揮します。以下のケースでは特に有効です:
- ジェネリックプログラミングやテンプレートメタプログラミング
- 数値計算や固定サイズの配列操作
- コンパイル時に決定可能な定数の計算
2. コードレビューとテスト
新しい機能を使用する際は、コードレビューとテストを徹底することが重要です。特に、constexpr関数の正しさを保証するために、ユニットテストを活用します。
#include <cassert>
constexpr int factorial(int n) {
return (n <= 1) ? 1 : (n * factorial(n - 1));
}
static_assert(factorial(5) == 120, "Factorial calculation is incorrect");
3. ドキュメント化
新しい機能を使用する際は、コードにコメントを追加し、ドキュメントを作成することで、他の開発者が理解しやすくなります。
/// コンパイル時にフィボナッチ数を計算するconstexpr関数
constexpr int fibonacci(int n) {
return (n <= 1) ? n : fibonacci(n - 1) + fibonacci(n - 2);
}
これらの注意点とベストプラクティスを守ることで、型推論とconstexpr関数を効果的に活用し、C++プログラムの品質とパフォーマンスを向上させることができます。
コードの可読性と保守性
型推論とconstexpr関数は、コードの可読性と保守性に大きな影響を与えます。これらの技術を適切に使用することで、コードベースの理解が容易になり、将来的なメンテナンスがしやすくなります。このセクションでは、これらの技術がどのようにコードの可読性と保守性に寄与するかを詳しく解説します。
型推論による可読性の向上
1. 簡潔なコード
型推論を使用することで、冗長な型宣言を省略でき、コードが簡潔になります。これは特に、長い型名やジェネリック型を使用する場合に有効です。
// 型推論を使用しない場合
std::map<std::string, std::vector<int>> data;
// 型推論を使用する場合
auto data = std::map<std::string, std::vector<int>>{};
この例では、auto
を使用することで、型宣言が簡潔になり、可読性が向上しています。
2. 一貫性の確保
型推論は、変数の型が初期化式から自動的に決定されるため、一貫性のある型を確保しやすくなります。これにより、型のミスマッチによるバグを防ぐことができます。
auto x = 10; // int型
auto y = 3.14; // double型
auto result = x + y; // resultの型はdoubleと推論される
constexpr関数による保守性の向上
1. 定数の一元管理
constexpr関数を使用することで、定数値を一元的に管理でき、変更が容易になります。定数値を変更する際に、関数の定義のみを修正すればよいため、保守性が向上します。
constexpr int getMaxSize() {
return 1024;
}
// 使用箇所
int buffer[getMaxSize()];
この例では、バッファサイズがgetMaxSize
関数で定義されているため、変更が必要な場合は関数の定義のみを修正すれば済みます。
2. バグの早期発見
constexpr関数はコンパイル時に評価されるため、コンパイル時にバグを検出できます。これにより、実行時のバグ発生を未然に防ぐことができます。
constexpr int square(int x) {
return x * x;
}
static_assert(square(3) == 9, "Square function is incorrect");
この例では、static_assert
を使用してコンパイル時にsquare
関数の正しさを検証しています。
ベストプラクティス
1. 適切なコメントの追加
型推論やconstexpr関数を使用する際は、適切なコメントを追加して、コードの意図を明確にすることが重要です。
// 半径から円の面積を計算するconstexpr関数
constexpr double circleArea(double radius) {
return 3.14159265358979323846 * radius * radius;
}
2. コーディング規約の遵守
チーム全体でコーディング規約を統一し、型推論やconstexpr関数の使用方法を明確に定義することで、コードの一貫性と可読性を向上させます。
まとめ
型推論とconstexpr関数は、コードの可読性と保守性を大幅に向上させる強力なツールです。これらの技術を適切に使用することで、効率的で保守しやすいコードベースを構築できます。適切なコメントやコーディング規約を導入し、一貫性のあるコーディングスタイルを維持することが重要です。
コンパイラの最適化
型推論とconstexpr関数は、コンパイラによる最適化を促進し、効率的なマシンコードの生成に寄与します。コンパイラがこれらの技術をどのように活用するかを理解することで、より高性能なプログラムを作成することができます。このセクションでは、コンパイラの最適化のメカニズムと具体的な効果を紹介します。
型推論による最適化
1. 型の決定による効率的なコード生成
型推論は、コンパイラが変数の型を自動的に決定するため、型に依存した最適化が可能になります。これにより、より効率的なマシンコードが生成されます。
auto x = 10; // xの型はintと推論される
auto y = 3.14; // yの型はdoubleと推論される
auto result = x + y; // resultの型はdoubleと推論される
この例では、コンパイラはresult
がdouble
型であることを推論し、適切な最適化を行います。
2. 冗長な型チェックの削減
型推論を使用することで、冗長な型チェックが不要になり、コンパイラの最適化プロセスが簡略化されます。これにより、コンパイル時間が短縮され、実行時パフォーマンスが向上します。
std::vector<int> vec = {1, 2, 3, 4, 5};
for (auto it = vec.begin(); it != vec.end(); ++it) {
// コンパイラはitがstd::vector<int>::iterator型であることを推論し、最適化する
std::cout << *it << " ";
}
constexpr関数による最適化
1. コンパイル時評価による最適化
constexpr関数は、コンパイル時に定数式を評価するため、実行時の計算コストを削減します。これにより、実行時パフォーマンスが大幅に向上します。
constexpr int factorial(int n) {
return (n <= 1) ? 1 : (n * factorial(n - 1));
}
constexpr int result = factorial(5); // コンパイル時に計算される
この例では、factorial
関数がコンパイル時に評価され、結果がresult
に格納されます。実行時には再計算が不要となり、パフォーマンスが向上します。
2. 定数の折りたたみ(Constant Folding)
コンパイラは、constexpr関数を使用することで定数の折りたたみを行います。これにより、複雑な計算が単純な定数に置き換えられ、コードの実行効率が向上します。
constexpr int a = 10;
constexpr int b = 20;
constexpr int sum = a + b; // コンパイル時に計算され、sumは30に置き換えられる
コンパイラの最適化技術
1. インライン展開
コンパイラは、頻繁に呼び出される小さな関数をインライン展開することで、関数呼び出しのオーバーヘッドを削減します。constexpr関数はインライン展開の候補となるため、パフォーマンスが向上します。
constexpr int add(int x, int y) {
return x + y;
}
int main() {
int result = add(3, 4); // コンパイラはadd関数をインライン展開する可能性が高い
std::cout << result << std::endl;
}
2. ループの最適化
型推論とconstexpr関数を使用することで、ループ内の定数計算がコンパイル時に行われ、ループの最適化が促進されます。これにより、ループの実行速度が向上します。
constexpr int N = 1000;
int main() {
int sum = 0;
for (int i = 0; i < N; ++i) {
sum += i; // コンパイラはNがconstexprであることを利用して最適化を行う
}
std::cout << sum << std::endl;
}
まとめ
型推論とconstexpr関数は、コンパイラによる最適化を大幅に助ける要素です。型推論により型が自動的に決定され、コンパイラが効率的なコードを生成できるようになります。また、constexpr関数はコンパイル時に評価されるため、実行時の計算コストが削減されます。これらの技術を適切に利用することで、C++プログラムのパフォーマンスを最大限に引き出すことができます。
演習問題
ここでは、型推論とconstexpr関数を実際に使ってみるための演習問題を提供します。これらの問題を解くことで、これらの技術をより深く理解し、実践的なスキルを身に付けることができます。
問題1: 型推論を使用したベクトル操作
以下のコードでは、std::vector
を使用して整数のリストを操作しています。型推論を使用してコードを簡潔にしてください。
#include <vector>
#include <iostream>
int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5};
std::vector<int>::iterator it = numbers.begin();
for (; it != numbers.end(); ++it) {
std::cout << *it << " ";
}
return 0;
}
解答例
#include <vector>
#include <iostream>
int main() {
auto numbers = std::vector<int>{1, 2, 3, 4, 5};
auto it = numbers.begin();
for (; it != numbers.end(); ++it) {
std::cout << *it << " ";
}
return 0;
}
問題2: constexpr関数を使用したフィボナッチ数計算
以下のコードでは、フィボナッチ数を計算する関数を定義しています。これをconstexpr関数に変更し、コンパイル時にフィボナッチ数を計算するようにしてください。
int fibonacci(int n) {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}
int main() {
int result = fibonacci(10);
std::cout << "Fibonacci(10): " << result << std::endl;
return 0;
}
解答例
constexpr int fibonacci(int n) {
return (n <= 1) ? n : (fibonacci(n - 1) + fibonacci(n - 2));
}
int main() {
constexpr int result = fibonacci(10);
std::cout << "Fibonacci(10): " << result << std::endl;
return 0;
}
問題3: 定数配列の生成
constexpr関数を使用して、コンパイル時に評価される定数配列を生成してください。配列には、最初の10個のフィボナッチ数を含めます。
#include <iostream>
int main() {
// ここに配列を定義
for (const auto& value : fibonacciArray) {
std::cout << value << " ";
}
return 0;
}
解答例
#include <iostream>
constexpr int fibonacci(int n) {
return (n <= 1) ? n : (fibonacci(n - 1) + fibonacci(n - 2));
}
constexpr int fibonacciArray[10] = {
fibonacci(0), fibonacci(1), fibonacci(2), fibonacci(3),
fibonacci(4), fibonacci(5), fibonacci(6), fibonacci(7),
fibonacci(8), fibonacci(9)
};
int main() {
for (const auto& value : fibonacciArray) {
std::cout << value << " ";
}
return 0;
}
問題4: 型推論とconstexpr関数の組み合わせ
型推論とconstexpr関数を組み合わせて、円の面積と周囲を計算するプログラムを作成してください。
#include <iostream>
constexpr double pi = 3.14159265358979323846;
constexpr double circleArea(double radius) {
return pi * radius * radius;
}
constexpr double circleCircumference(double radius) {
return 2 * pi * radius;
}
int main() {
// ここにコードを追加
return 0;
}
解答例
#include <iostream>
constexpr double pi = 3.14159265358979323846;
constexpr double circleArea(double radius) {
return pi * radius * radius;
}
constexpr double circleCircumference(double radius) {
return 2 * pi * radius;
}
int main() {
auto radius = 5.0;
auto area = circleArea(radius);
auto circumference = circleCircumference(radius);
std::cout << "Radius: " << radius << std::endl;
std::cout << "Area: " << area << std::endl;
std::cout << "Circumference: " << circumference << std::endl;
return 0;
}
まとめ
これらの演習問題を通じて、型推論とconstexpr関数の基礎を実践的に理解し、効果的な使い方を学ぶことができます。これらの技術をマスターすることで、C++プログラムの効率性、可読性、およびパフォーマンスを大幅に向上させることができます。
まとめ
本記事では、C++の型推論とconstexpr関数について詳しく解説し、それらを効果的に組み合わせる方法を紹介しました。型推論はコードの可読性を向上させ、冗長な型宣言を省略することで開発効率を高めます。一方、constexpr関数はコンパイル時に定数式を評価するため、実行時の計算コストを削減し、プログラムのパフォーマンスを最適化します。
型推論とconstexpr関数を組み合わせることで、コードがより簡潔で直感的になり、メンテナンスも容易になります。さらに、これらの技術を活用することで、コンパイラの最適化が促進され、効率的なマシンコードが生成されます。
演習問題を通じて、型推論とconstexpr関数の実践的な使用方法を学びました。これらの技術をマスターすることで、より高性能で保守しやすいC++プログラムを作成できるようになります。
今後の開発において、型推論とconstexpr関数を適切に活用し、効率的なコーディングを心がけてください。これにより、プログラムの品質とパフォーマンスを大幅に向上させることができるでしょう。
コメント