質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.47%
C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

2回答

2446閲覧

参照の使い方や、引数として不可な物、一時オブジェクト

big000

総合スコア18

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

0クリップ

投稿2019/08/04 18:39

c++の技術評論社「ポケットリファレンス」を読んで勉強しています。
参照に関して、不明な点がありましたので、ご教授ねがいたいです。

基本文法のところで、
参照は、関数の仮引数および戻り値でも使用できます。
とあり、以下のコードが例として挙げられています。

const int& square(const int &v){//仮引数と戻り値は参照 static int result=v*v;     //静的宣言をせいておかないと、                  //戻った後破棄されてしまう。 return result; }

関数の仮引数を参照にすることで、無駄なコピーが発生しません。これは特に大きなオブジェクトを関数へ渡す場合に、コピーによるメモリ消費を抑えられ、コピー処理に時間もかからないため有効です。
参照は、ポインタと違い、一時オブジェクトや定数もconst参照へ渡せます。

Struct S{ int data=0; }; void fr(S&v); void fr(int &v);  void fcr(const S&v); void fcr(const int&v);  S s; int a=42; fr(s); //オブジェクトを参照で渡す fr(a); //オブジェクトを参照で渡す //fr(S());//エラー。一時オブジェクトを参照で渡すことはできない。 //fr(42);//エラー。整数リテラルを参照で渡すことはできない。 fcr(S());//一時オブジェクトをconst参照で渡すことが可能。 fcr(42);//整数リテラルをconst参照で渡すことが可能。 void fp(S*v); void fp(int*v); //fp(&S());//エラー。一時オブジェクトのアドレスは取得できない。 //fp(&42);//エラー。整数リテラルのアドレスは取得できない。

とありました。。

①まず

const int& square(const int &v)

仮引数で、参照を使うのはわかりますが、int &squareと参照にしているのは、
戻り値==「int型関数square」という意味であっていますでしょうか。

②そして

void fr(S&v); void fr(int &v);  void fcr(const S&v); void fcr(const int&v); 

例えば一番上の場合、クラスSオブジェクト○○を引数にとる関数frを宣言していると思いますが、
この場合のvをなんというのでしょうか。

③そして、下のコードに関して

//fr(S());//エラー。一時オブジェクトを参照で渡すことはできない。 //fr(42);//エラー。整数リテラルを参照で渡すことはできない。 fcr(S());//一時オブジェクトをconst参照で渡すことが可能。 fcr(42);//整数リテラルをconst参照で渡すことが可能。

まず、一時オブジェクトとはなにかがあまり理解できません。
なぜ、一時オブジェクトや整数リテラルを参照で渡すことができないのかが、わかりません。

整数リテラルは、定数とC言語の本を読んだので、
だからconstで渡せることができるのでしょうか。

④↓以下のコードは、整数リテラルにアドレスはないからでしょうか。

void fp(S*v); void fp(int*v); //fp(&S());//エラー。一時オブジェクトのアドレスは取得できない。 //fp(&42);//エラー。整数リテラルのアドレスは取得できない。

以上お願いします(--)

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

こんにちは。

①戻り値==「int型関数square」という意味であっていますでしょうか。

この定義は関数を返すような定義にはなっていませんね。従って、間違っています。
恐らくもう少し異なることを聞かれたいのだろうとは思うのですが、推測できません。

関数squareの戻り値の型は、int型へのconst参照です。

②この場合のvをなんというのでしょうか。

う~~ん、私も「仮引数」に一票入れます。
恐らくもう少し異なることを聞かれたいのだろうとは思うのですが、推測できませんした。

例えば一番上の場合、クラスSオブジェクト○○を引数にとる関数frを宣言していると思いますが、

違います。クラスSオブジェクトの参照を引数にとる関数frを宣言しています。

③まず、一時オブジェクトとはなにかがあまり理解できません。

計算処理を行うための作業領域です。
例えば、d = a + b - c;を計算する際には、ざっくり下記のような処理が行われます。

  1. 一時オブジェクトを獲得し、
  2. aの内容を取り出して一時オブジェクトへ入れ、
  3. 一時オブジェクトとbを足した結果を一時オブジェクトへ入れ、
  4. 更に一時オブジェクトからCを引いた結果を一時オブジェクトへ入れ、
  5. その一時オブジェクトの内容をdへ入れ、
  6. 最後に一時オブジェクトを開放します。

一時オブジェクトはこのようなものです。(アセンブラを知っている人なら、アキュムレータのことかな?と理解すると思います。)
なお、C++の一時オブジェクトはint型等の基本型だけでなく、クラス・インスタンス等の高度なものにも対応しているのでより複雑ですが、基本は同じです。(クラス・インスタンスは一般にアキュムレータには入らないので)

なぜ、一時オブジェクトや整数リテラルを参照で渡すことができないのかが、わかりません。

constでない参照で指している領域へ書き込むようなコードを書いてもコンパイラはそれをエラーとはみなしません。(そりゃそうですよね。)
逆にconst参照が指している領域へ書き込むようなコードを書くとコンパイラはエラーと指摘します。これはバグを減らす先人の知恵です。const参照とすることで、ここには書き込めないとプログラマー自身が宣言しておけば、間違って書き込んだ時、コンパイラが間違いを指摘してくれるのでバグが減るという仕組みです。

●以上を踏まえて、まずは整数リテラルについて。
整数リテラルへ書き込みしてしまうと痛すぎます(昔それが可能な処理系が存在しました)ので、それができないようにするため、constでない参照で定数を受け取れないようにしているのです。
(通常の参照への書き込みはエラーにならないので、間違って書き込んでもコンパイラはエラーを指摘できない。なお、現代的なパソコンでは無理やり書き込むと大抵OSが不正書き込みを検出してくれますのでダブルチェックが機能しています。)

●さて、問題は一時オブジェクトを参照で受けることができないのか?です。
実は、Visual C++ 2013はクラス・インスタンスの一時オブジェクトならconstでない参照で受けることができました。(今もオプションを設定すれば受けることができる筈です)
基本型の一時オブジェクトは原則として「アキュムレータ」で処理されますがアキュムレータを参照することは事実上できません。クラス・インスタンスは通常はメモリに保存される筈ですから参照できます。
つまり、実装上可能なことは許したのだろうと思います。

gccは、int型等の基本型、および、クラスのどちらもconstでない参照では受けることができません。
「基本型とクラスは原則として同じ振る舞いにする」のがC++の基本思想なのでそれに合わせたのではないかと思います。

規格制定時にマイクロソフトとGNUが議論して、GNUに軍配が上がったのだろうと思います。

④↓以下のコードは、整数リテラルにアドレスはないからでしょうか。

処理系や値によってアドレスがある場合もあればない場合もあります。
最終的にマシン語に落ちますが、CPUによっては、例えば0を設定する場合に、0を代入する命令以外にも、0クリアするという命令もあります。一般に後者の方が高速です。このようなケースでは、アドレスは存在しません。
更に、そのような命令が存在しないような値(例えば、38938217)などが複数の箇所で使われていたとします。プログラマーはそれぞれが同じアドレスになることを期待するかもしれません、そのような処理を行うのは処理系に取って負荷が掛かります。そんな無駄な手間をかけるほど重要な部分ではない筈ですから、定数のアドレスは取得できないことにしたのだろうと思います。

一時オブジェクトも同様と思います。アキュムレータに入っているとアドレスそのものが存在しません。
仕様を一様にするために、アドレスのあるような一時オブジェクトもアドレスを取得できないことに定めたのだろうと思います。

なお、const参照で受け取れば、その参照のアドレスを取得できますね。一見元の定数や一時オブジェクトのアドレスを指しているように見えますが、必ずしもそうではないです。元の値がアドレスを持っていない時は、わざわざメモリへ一旦ストアしてそのアドレスを返している処理系を見たことがあります。(VC++だったと思いますが、gccも同様かも)

投稿2019/08/05 03:55

Chironian

総合スコア23272

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

big000

2019/08/05 04:46

③.④に関して、丁寧な説明ありがとうございました。 バグを防ぐために、隠しているということで理解できました。 ありがとうございました。 ①に関して、 const int& square(const int &v) 引数でint型のvを参照しているのは分かりますが、 squareの型がなぜ、int&かわからないという意味でした。
Chironian

2019/08/05 04:58

関数squareの型(シグネチャ)は、int&ではありません。const int& (const int&)ですよ。 http://stlalv.la.coocan.jp/OverloadOverride.html#SIGNATURE 因みに、関数squareの戻り値の型も、int&ではありません。const int&です。 C++の型システムは本当に複雑なので、推測で議論すると発散しやすいので、用語を正確に使わないとなかなか議論が成立しません。悩ましい部分なのです。
big000

2019/08/05 08:59

よく分かりました ありがとうございました。(~~)
guest

0

cpp

1const int& square(const int &v)

戻り値はint型変数の参照です。
つまり、int型変数の参照を返す関数と言えます。

仮引数って言います。

まず、一時オブジェクトとはなにかがあまり理解できません。

簡単に言えば、名前がないものが一時オブジェクト。
例えば

cpp

1auto s = S(); // sという名前があり、あとでアクセスできるので一時オブジェクトではない。 2S(); // 名前が無いので一時オブジェクト。

右辺値参照を使えば一時オブジェクトや整数リテラルを参照で渡すことができます。
https://wandbox.org/permlink/djGkCPmwKBrr5s33

cpp

1#include <iostream> 2#include <string> 3void f(int&& i){ 4 std::cout <<i << std::endl; 5} 6 7void g(std::string&& s){ 8 std::cout <<s << std::endl; 9} 10 11int main() { 12 g(std::string("abc")); 13 f(20); 14 return 0; 15}

アドレスは存在します。
https://wandbox.org/permlink/pM16XLx0jwYxTphp

cpp

1#include <iostream> 2#include <string> 3void f(int&& i){ 4 std::cout << i << std::endl; 5 std::cout << &i << std::endl; 6} 7 8void g(std::string&& s){ 9 std::cout << s << std::endl; 10 std::cout << &s << std::endl; 11} 12 13int main() { 14 g(std::string("abc")); 15 f(20); 16 return 0; 17}

投稿2019/08/04 19:42

yokuda

総合スコア138

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

big000

2019/08/05 09:09

簡潔なご回答ありがとうございました。一時オブジェクトは上の方と合わせて理解できました。 続けて質問なのですが、右辺値参照のことがよく理解できません。 &&をつけた、一時的なものという認識ですが、いまいち分かりません。 https://cpprefjp.github.io/lang/cpp11/rvalue_ref_and_move_semantics.html ①右辺値参照とは ②左辺値参照との違いとは ③ムーブセマンティクスとは 連投ですが、御教えねがいたいです
yokuda

2019/08/05 11:23

1. > 誤解を恐れずに言えば、右辺値とは名前をもたない一時的なオブジェクトである。 このような値を参照できるようにするためのものです。 2. 右辺値の参照か左辺値の参照か。 3. cpprefjpに結構丁寧に書かれていますよ。 どの辺がわからないですか?
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.47%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問