C++の(左辺値)参照を参照渡し

このQ&Aのポイント
  • C++の(左辺値)参照を参照渡しする際にNGなのかを確認したい
  • 左辺値参照を左辺値参照渡しするコードは規格上OKなのか
  • 左辺値参照を左辺値参照渡しする際の挙動を確認するコードの例
回答を見る
  • ベストアンサー

C++の(左辺値)参照を参照渡し

おそらく基本的なところなのですが 参照を参照で渡すのはNG…? という記述をどっかで見たような見なかったような気がするのですが 再度確認しようと思うも見つからず。 気のせいかもしれません。 または そのときの文章では「参照」という言葉が別の意味で使われていたかもしれませんが 以下のように、左辺値参照を左辺値参照渡しするコードはconst参照、非const参照問わず規格上OKでしたっけ? #include <stdio.h> void func(int& n){ if ( n%7 ){ n+=9; func(n); } else return; } int main(void){ int i = 1; func(i); printf( "%d", i ); return 0; }

質問者が選んだベストアンサー

  • ベストアンサー
  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.2

あ~, 「『int への参照』の参照は NG」で誤解させちゃった.... すみません, これはおかしいです. 参照への参照は「作れない」, といった方が適切でした. ほかにも, 「参照へのポインタ」とか「参照の配列」も作れません. と冒頭で断っておいて, と. あんまりまじめに参照を使うこともない (関数の引数/返り値以外ではあんまり使ってないなぁ) のでやっぱり規格 (FDIS) を見たりするわけですが, 実は 7 は OK だったりします. T が「typedef, テンプレート引数または decltype」で参照を含む場合には T& あるいは T&& も合法です. その場合, T&& なら T と同じ参照を表し, T& は強制的に左辺値参照となります. 7 の場合 T = int& で T& という形ですから T& = int& です (この場合は T&& も int&). 質問文のコードを合法にしないとまずいのは, operator = をオーバーロードするときを考えればわかると思います. つまり, X::operator = はたいてい X &X::operator =(const X &) のように宣言しますよね. とすると, X a, b, c; a = b = c; とやっちゃうと a.operator =(b.operator =(c)) と同じで「b.operator =(c) が返す参照をそのまま a.operator = に参照引数として渡す」ことになります. 「参照を参照で渡す」のを NG にしちゃうとこれも NG になってしまいます.

LongSecret
質問者

お礼

ありがとうございます♪ >「参照へのポインタ」とか「参照の配列」も作れません. そのへんはOKです。たぶんw >7 の場合 T = int& で T& という形ですから T& = int& です (この場合は T&& も int&). あ、そうなるんですか。 それはどの部分に書いてありましたでしょうか?(あるいは基本的なことなんでしょうか) >質問文のコードを合法にしないとまずいのは~ なるほど、基本的な使い方では問題はないということですね。 う~~んしかし http://boost.cppll.jp/HEAD/libs/functional/binders.html#refref (↑たぶん文字エンコーディングは 日本語(EUC-JP)) を要約して #include <iostream> #include <vector> int main(void){ struct Foo{ void bar(std::ostream&){} }; std::bind2nd(std::mem_fun_ref(&Foo::bar), std::cout); } みたいな感じでコンパイルエラーになったり http://www.devx.com/tips/Tip/13219 を要約して #include <list> struct Node; std::list <Node&> ln; int main(void){} でコンパイルエラーになる理由が、追いまくってみたものの、現状 良く分かっていません。 typedef typename なんとか::メンバ& 新しい型名; みたいな部分が絡んでる?のかな?と思ったり思わなかったりなのですが 下の7の例が T&& → int& となって、こっちが出来なくなる理由が分かりません。(あるいは別の部分が問題?) これらはどういう違い、あるいは理由があるんでしょうか? (なにもインクルードせずに10行以内程度で再現できるコードが出来ればそれをみたいです)

LongSecret
質問者

補足

あ、もしかして項目7が「OK」なのって「C++11であれば」ってことでしょうか? そうなると下の std::bind2ndやstd::listの件に関しては別の個所が問題なのかな…?? あれ?でもやっぱりちょっとおかしいような 試すの忘れてたのでVC++2008(C++03のはず)で試したら 7は通りませんでした。 2010では通りました。 この「通るか通らないか」は「準拠状況込み」の話なんでしょうかね。

その他の回答 (2)

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.3

ざっと調べてみましたが, 11 で導入された可能性があります. 98 を斜め読みしてみたものの, 対応する文言は見つからず. std::list については要素が CopyConstructible でなければならない (つまり「& でアドレスが取れる」必要がある) ので参照は無理じゃないかなぁ.

LongSecret
質問者

お礼

>std::list については要素が CopyConstructible でなければならない (つまり「& でアドレスが取れる」必要がある) ので参照は無理じゃないかなぁ. なるほど、参照へのポインタが無理なのでってことですね。 綿密に調べてみました。 4段階の継承とallocatorを持ってて 大変でしたが 整理したときにミスってなければ 最終的、具体的には、これらの理由は見つけました。 template<class T > struct list { T* Alloc(){ return (T*)operator new( sizeof(T) ); } struct NODE { T _Myval; } head; }; struct Node {}; list<Node&> ln; //list<Node*> ln; や list<Node> ln;なら可 確かに これは単純に、無理ですねw でも 「A Reference to a Reference is Illegal」 が絡む箇所あったかな? と思ったので、2008でも確認してみたら 2010ではエラー数6なのに対して エラー 19、警告 18 と、大幅に内容に開きがありました。 また #include <iostream> #include <vector> int main(void){ struct Foo{ void bar(std::ostream&){} }; std::bind2nd(std::mem_fun_ref(&Foo::bar), std::cout); } の方は 2010だと error C2440: '初期化中' : 'const std::ostream' から 'std::basic_ostream<_Elem,_Traits> &' に変換できません。 一つのみなのに対し(しかも↑の内容そのものになってしまうなら確かに当然無理) 2008ではエラー 4つでした。 これらから、まだ記述を見つけてないだけだとしたら 2008と2010では準拠レベルが違うか、C++11で「参照の参照は参照」が正式に公認になった もし言語仕様で明確に言及されてないようなら、VC++2010の機転か何かの可能性がある ただし問題となり得る「参照の参照」というのは、「やはりテンプレートかtypedefとかのメタプログラミング関連?」 でもって とりあえず 質問文の内容や 項目6の内容や struct X{ X &X::operator=(const X &){ return* this; } static void f(){ X a, b, c; a = b = c; } }; がC++11でなくても合法 というのは確定的なので ふつーに自分でそんなにややこしくない使い方する分には 何ら問題なさ気、って判断しといていい、でしょうかね。

LongSecret
質問者

補足

おお VC++2008でみれました typedef int& IR; int a = 0; IR b = a; IR& c = b; error C2529: 'c' : 参照への参照は無効です。 (とerror C2440: '初期化中' : 'int' から 'IR (&)' に変換できません) とか template <class T> struct DATA{ T& t; }; typedef int& IR; int a = 0; IR b = a; DATA<IR> d = {b}; error C2529: 't' : 参照への参照は無効です。 error C2440: '初期化中' : 'int' から 'IR (&)' に変換できません。 あー、こうしてみると やっぱ「参照の参照」って あくまで型の話ってことでOKですね。 なお、これらのコードはVC++2010なら通りました。

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.1

あ, 今 C++11 の FDIS 見てたらなんとなく言いたいことがわかった気がする. 「参照を参照型の引数として関数に渡す」ことはまったく問題ありません... というか, それは否定されるといろいろめんどくさい. 「参照の参照」は作れません. つまり int への参照 はできる (int& だ) が 「int への参照」の参照 は NG.

LongSecret
質問者

お礼

どうもありがとうございます♪ あ~、そんな感じの書かれ方だった気がします。 でもそんときも「コードを見なかったんで正確に分かんなかった」んですがw コード込みでしっかり確認しておきたいので C++11込みだと これらの認識で全部OKでしょうか? 1. int&& a = 1; もし「参照の参照を意図したい表記」として こういう風に書くと、この場合C++03では「NGではなく出来ない。」 C++11では文法上OKになるが、参照の参照ではなく、この場合右辺値参照となる。 2. int a = 1; int&& b = a; 右辺値参照には左辺値をそのまま代入出来ない。C++11でも不正。 3. int a = 1; int&& b = std::move(a); C++11では合法。 4. int& a = 1; 非const参照は右辺値で初期化できない。 どちらでもエラー。 5. const int& a = 1; あるいは template <class T> T func(T&& t){ return t+1; } const int& a = func(1); など const参照の場合は、特別に初期化に右辺値をとれる。 生存期間はこの参照のそれと同じ。 6. void func(const int& i){ printf( "%d", i ); } int i=6; int& a = i; func(a); 質問文のコードと同じく、まったくの合法、ということになる。 7. template <class T> void func(T& t){ ++t; } int a = 9; func<int&>( a ); printf("%d", a ); tの型は「int&の参照」となるので これがNG。 ということでしょうか? つまり「参照の参照は"NG"("不可"ではなく)」 と言われるときの「参照の参照」とは こういう風にテンプレートを使った時の、それに限る という事で大丈夫でしょうかね?

関連するQ&A

  • C言語の参照はずしについて

    ソートのプログラムなんですが #include <stdio.h> #include <stdlib.h> int comp(const void *, const void *); int main() { int i; int test[6] = {10, 8, 2, 6, 4, 0}; qsort(test, (size_t)6, sizeof(int), comp); printf("\n"); for (i = 0; i < 6; i++) printf("%d\n", test[i]); return 0; } int comp(const void *a, const void *b) { static int i = 1; printf("%02d--%d,%d\n", i, *(int *)a, *(int *)b); i++; return (*(int *)a - *(int *)b); } 最後のreturnの()の中身がよくわかりません。「参照はずし」という事をしてるらしいんですが「参照はずし」とは何ですか意味も教えてください。

  • 関数 左辺値 参照 返り値 

    こんにちは。宜しくお願い致します。 > たとえば,下のサンプルプログラムでは関数fが参照を返すようになっているため,この関数fを代入の左辺においた,f(a, n) = 10;のような式が許される. > > #include <iostream> > using namespace std; > > int& f(int* a, int n) > { (2)~ return a[n]; > } > > int main(void) > { > const int n = 5; > int arr[n] = { 1, 2, 3, 4, 5 }; (1)~ f(arr, 3) = 10; > for (int i = 0; i < n; ++i) > cout << arr[i] << endl; > return 0; > } 上のサンプルプログラムで左辺値に参照を返す関数が(1)~あるのですが 動作が分りません。 関数内部の(2)~return a[n];というところで、関数の返り値int&の参照はa[n]という変数になるので、(1)~の結果としてa[n]に10が代入されるのでしょうか?教えてください。

  • c言語で大きな値の階数を求めたいのですが

    c言語で関数を用いてn!を求めるプログラムを作ったのですが、 nの値が大きくなると0という値になってしまって正しい値が出てきません。 プログラムをどの様に修正したらきちんとnの値が大きくなっても 正しく値が表示されるでしょうか? ソースはこちらです。 #include <stdio.h> int func(int i); int func(int i){ if(i == 0) return 1; else return (i*func(i-1)); } void main(){ printf("%d",func(90)); /*90!を求める*/ } よろしくお願いします。

  • C言語について

    次のような問題です。 問 自然数nを入力し、nを3で割って割り切れるかどうかを判定し結果を表示する。「割り切れる」、「1余る」、「「2余る」のいずれかが入るものとする。 このようなものをつくりました。 #include<stdio.h> int main(void) { int n; printf("自然数:"); scanf("%d",&n); if(n==0){ printf("割り切れる\n"); }else if(n==1){ printf("1余る\n"); }else{ printf("2余る"); } return(0); } これで合っているかよろしくお願いします。

  • c言語のフローチャートについての質問です

    #include <stdio.h> int main (void) { int n; for (n=1900; n<2000; n++) { if (n%4==0 && n%100!=10) printf ("%d",n); else if(n%400==0) printf ("%d",n); } printf("\n"); return 0; } をどなたかフローチャートに直してください JIS規格のものでお願いします

  • c言語のフローチャートについての質問です

    #include <stdio.h> int main (void) { int n; for (n=1900; n<2000; n++) { if (n%4==0 && n%100!=10) printf ("%d",n); else if(n%400==0) printf ("%d",n); } printf("\n"); return 0; } をフローチャートに直したいのですがいまいち方法が解りません、どなたか詳しい回答お願いします JIS規格のものでお願いします

  • funcの値

    こんにちは! 以下のプログラムですがいまひとつ流れが 分からずにいます、5を入力すると120が出力 されるのですが、(5-1)*5なので20ではないのでしょうか?? どなたか宜しければ教えてくださいm(_ _)m #include <stdio.h> double func(double n){ if ( n > 0 ) { return func ( n - 1 ) * n; } else { return 1; } } int main(){ int n; scanf("%d",&n); printf("%.0f\n",func(n)); return 0; }

  • (void *)と&の違い

    #include<stdio.h> void * func(void *p){ printf("□■□func開始□■□\n"); printf("pのアドレス = %p\n",p); printf("p = %d\n",(int)p); (int)p += 100; printf("p = %d\n",(int)p); printf("□■□func開始□■□\n"); return NULL; } int main(void){ int number = 30; printf("numberのアドレス = %p\n",&number); func((void *)number);★1 return 0; } -------------------------------------------------------------- #include<stdio.h> void * func(int *p){ printf("□■□func開始□■□\n"); printf("pのアドレス = %p\n",p); printf("p = %d\n",*p); *p += 100; printf("p = %d\n",*p); printf("□■□func開始□■□\n"); return NULL; } int main(void){ int number = 30; printf("numberのアドレス = %p\n",&number); func(&number);★2 return 0; } 上記の2つは同じ結果になるのですが★1と★2のそれぞれの違いがわかりません。どなたかご教授をお願いします。

  • C言語

    forの直後で1+2+3+4+5+・・・・・・・と加算し続ける式がわからないので教えてください。 #include<stdio.h> int main(void) { char moji; int i,sum; printf("正の整数を1から順に加算します。n\"); printf("加算を開始してよろしいですか。(Y=実行。N=終了)\n"); moji=getchar(); if(moji==y) { for(i=2;sum>=1001;i++) { この部分がわかりません; printf("加算値は%dです。¥n",sum); } }else if(moji=='n'){ printf("終了します。\n"); }else{ printf("YまたはNを入力してください。\n"); } return 0; }

  • C言語の初歩的な質問

    質問1----------------------------------------- #include<stdio.h> int main() {  int a;  char b[10];  for(int i = 0;i < 2;i++){   scanf("%d",&a);   printf("整数%d\n",a);  }  scanf("%s",b);  printf("文字列%s\n",b); } /* この際に、例えばabと入力すると結果が 整数-858993460 整数-858993460 文字列ab となり整数入力を無視して進められるのはなぜでしょうか? */ 質問2----------------------------------------- #include<stdio.h> void func(int**); int main() {  int *p;  func(&p);  printf("%d",*p); } void func(int **pp) {  int n = 10;  *pp = &n; } /* func関数のnはスコープからはずれて変数の寿命がなくなるはずなのにprintfで表示されるのはなぜでしょうか? */ ---------------------------------------------- C言語は一冊の本とネットの入門サイトをかじった程度の理解です。