質問内容
私は場合分けを減らすためにしばしば引数にコンストラクタのインスタンスを渡します.
引数にコンストラクタのインスタンスを渡している例や説明をあまりにも見かけないので心配になり質問しました.
エラーが出るわけではないですが,このような書き方はやめた方が良いのでしょうか.
該当のソースコード
c++
1#include <vector> 2void func(std::vector<int> vec) { 3 return; 4} 5int main() { 6 func(std::vector<int>()); 7 return 0; 8}
追記
以下の書き方はいずれも正しいのでしょうか.
c++
1 std::vector<int> vec1; 2 // std::vector<int> vec2(); 3 std::vector<int> vec2{}; 4 std::vector<int> vec3 = std::vector<int>(); 5 std::vector<int> *pvec = new std::vector<int>(); 6 delete pvec;
以下のような質問にはグッドを送りましょう
- 質問内容が明確
- 自分も答えを知りたい
- 質問者以外のユーザにも役立つ
グッドが多くついた質問は、TOPページの「注目」タブのフィードに表示されやすくなります。
気になる質問をクリップする
クリップした質問は、後からいつでもマイページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
下記のような質問は推奨されていません。
- 間違っている
- 質問になっていない投稿
- スパムや攻撃的な表現を用いた投稿
適切な質問に修正を依頼しましょう。
回答3件
1
ベストアンサー
こんにちは。
C++では、
C++
1func(std::vector<int>()); // ①
と
C++
1std::vector<int> vec; 2func(vec); // ②
では、同じように見えてかなり異なる意味付けがなされています。
前者は、式のように見えませんが実は式を渡しています。後者は変数を渡しています。
式と変数の大きな差は、ざっくり書くと(一部の例外を除き)結果が右辺値(値)か左辺値(容器)かの差です。
右辺値と左辺値の最大の差は、そこへ値を「代入」できるかどうかです。普通は右辺値に値を代入することはできませんが、左辺値は普通に値を代入できます。(a=a+123;
において a は左辺値、a+123の計算結果は右辺値です。)
関数の仮引数はコピー渡しなら右辺値、左辺値共に受け取れます。ご提示されているfoo()関数はご存知のようにコピー渡しですから、上記の前者・後者どちらとも受け取れます。ただし、コピー渡しですから当然コピーが発生します。int型等の小さな変数を引き渡す時に好ましい渡し方です。
さて、大きな変数を渡す時はコピーのコストを嫌うため参照渡しが好まれます。この時に右辺値、左辺値が問題になります。
ノーマルな参照型の仮引数は左辺値しか受け取れません。
constな参照型の仮引数は左辺値、右辺値共に受け取れますが、constですので内容を変更できません。
右辺値参照型の仮引数は右辺値を受け取れ内容も変更できますが、左辺値を受け取れません。(右辺値は式の計算結果ですのでその内容を変更しても意味はないので内容を変更する目的で使われることはあまりなさそうです。)
C++
1void foo0(std::vector<int>& vec) { } // ノーマルな参照渡し 2void foo1(std::vector<int>const & vec) { } // constな参照渡し 3void foo2(std::vector<int>&& vec) { } // 右辺値参照渡し
整数型等の小さな変数を渡す時は、foo の形式がよく使われます。
大きな変数を渡すだけの時は foo1 の形式が良く思います。
渡された引数を加工した時は、foo0が良く使われます。
foo2は比較的最近(といっても2011年ですが)使えるようになった使い方です。
あまり頻繁には使いませんがリソースの所有権を移動したい際に便利です。身近な例ではstd::unique_ptr
でしょう。
エラーが出るわけではないですが,このような書き方はやめた方が良いのでしょうか.
fooの形式ではコピーが発生しますので、std::vector等の巨大になりうる変数を渡す時にはあまり使われません。その意味で避けた方が好ましいと思います。
さて、ベターCとしてC++を使っている人は foo0 の形式を使う場合が多いと思います。この時①の渡し方ができません。
C++のちょっと深い部分を使い始めた方は foo1 の形式を使う場合が多いと思います。これは②の渡し方もできますが、どちらかというと「渡された変数の内容を変更しませんよ」という意味を込めて使うので②のケースを見かけることは少ないかもしれませんが、普通に使って問題ないです。
foo2 は歴史も浅いですし、最近出現したということは使う場面も多くはない(もし非常に多いならもっと初期に出現している筈)ので、あまり見かけなくても不思議ではありません。
追記
vec2は構文は合ってますが、意図とは合っていないと思います。
これはstd::vector<int>
を返却する関数vec2()
の前方参照宣言と解釈されます。
投稿2019/06/26 02:48
編集2019/06/26 02:51総合スコア23251
良いと思った回答にはグッドを送りましょう。
グッドが多くついた回答ほどページの上位に表示されるので、他の人が素晴らしい回答を見つけやすくなります。
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
このような回答には修正を依頼しましょう。
回答へのコメント
1
引数にコンストラクタを渡している例や説明をあまりにも見かけないので心配になり質問しました.
コンストラクタは渡されていません。その場で生成したインスタンスを渡しているだけです。
なお、std::vector
を引数に取ると、毎回コピーされます。狙ってやるのならともかく、コピー処理が不要なら、参照引数としたほうがいいでしょう(const std::vector<int> & vec
のようにすれば、参照経由での書換も不可能になります)。
投稿2019/06/25 23:24
総合スコア141508
良いと思った回答にはグッドを送りましょう。
グッドが多くついた回答ほどページの上位に表示されるので、他の人が素晴らしい回答を見つけやすくなります。
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
このような回答には修正を依頼しましょう。
回答へのコメント
2019/06/26 00:48
0
私は場合分けを減らすためにしばしば引数にコンストラクタを渡します.
コンストラクタを渡してはいません。
呼び出し側でコンストラクタによって初期化された一時オブジェクトを
呼び出された func の vec が共用してアクセスします。
呼び出し側でコンストラクタによって初期化された一時オブジェクトを使い、
コピーコンストラクタで func の仮引数 vec を初期化するということでは
ありません。
func の vec を呼び出し側のコンストラクタで初期化すると解釈しても構いません。
引数にコンストラクタのインスタンスを渡している例や説明をあまりにも見かけないので心配になり質問しました.
<algorithm> の sort を使って vector<int> を降順にソートするとき、
sort(vec.begin(), vec.end(), greater<int>()); と書きます。
greater<int>() は、greater<int>クラスのインスタンスで、このクラスには、
bool operator()(int a, int b) { return a > b; }
いうメンバ関数が定義されてるので、
greater<int>() は関数オブジェクトと呼ばれます。
C++
1vector<int> vec; 2func(vec);
この場合、呼び出し側の vec の値を使い、コピーコンストラクタで
func の vec を初期化します。
値渡しです。変数渡しではありません。
void func(vector<int>& vec) { と宣言されていれば、
参照渡しであり、これを変数渡しといっても構いません。
vector<int> vec; で宣言された vec は左辺値です。
vector<int>() で生成された一時オブジェクトですら左辺値です。
だから、次のプログラムはエラーになりません。
C++
1#include <vector> 2 3int main() 4{ 5 std::vector<int>() = { 1, 2, 3 }; 6}
投稿2019/06/26 10:35
総合スコア8087
良いと思った回答にはグッドを送りましょう。
グッドが多くついた回答ほどページの上位に表示されるので、他の人が素晴らしい回答を見つけやすくなります。
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
このような回答には修正を依頼しましょう。
関連した質問
Q&A
解決済
codefestival2015予選A D問題 作成した二分探索のコードが通らない
回答1
クリップ0
更新
2023/02/01
Q&A
解決済
整数 Nが与えられたときの2^Nを10で割った余りを計算しなさい
回答3
クリップ0
更新
2023/01/19
Q&A
受付中
2つの三次元ベクトルから角度を求める問題。
回答2
クリップ0
更新
2023/02/06
Q&A
解決済
エラーが出ずにコンパイル終了しないコードについて
回答1
クリップ0
更新
2023/02/05
Q&A
受付中
Rustで2つの値の入力を1行に行う。
回答2
クリップ0
更新
2023/02/05
Q&A
解決済
classの中の、整数と文字を一緒にアウトプットするメソッドの記述について
回答3
クリップ0
更新
2023/01/29
Q&A
解決済
for文の中が途中から実行されない
回答1
クリップ0
更新
2023/02/04
Q&A
解決済
Raspberry Pi Picoの内蔵温度センサーから温度を取得したい
回答1
クリップ0
更新
2023/02/04
同じタグがついた質問を見る
STL(Standard Template Library)は、ジェネティックコンテイナー、イテレーター、アルゴリズム、そして関数オブジェクトのC++ライブラリーです。
オブジェクト指向言語において、オブジェクトを生成時に呼び出され、データの初期化などを行なう関数・メソッドのことである。
関数(ファンクション・メソッド・サブルーチンとも呼ばれる)は、はプログラムのコードの一部であり、ある特定のタスクを処理するように設計されたものです。
C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。