アプリ版:「スタンプのみでお礼する」機能のリリースについて

おそらく基本的なところなのですが

参照を参照で渡すのは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;
}

A 回答 (3件)

あ~, 「『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 になってしまいます.

この回答への補足

あ、もしかして項目7が「OK」なのって「C++11であれば」ってことでしょうか?

そうなると下の
std::bind2ndやstd::listの件に関しては別の個所が問題なのかな…??

あれ?でもやっぱりちょっとおかしいような

試すの忘れてたのでVC++2008(C++03のはず)で試したら
7は通りませんでした。

2010では通りました。
この「通るか通らないか」は「準拠状況込み」の話なんでしょうかね。

補足日時:2012/03/08 09:47
    • good
    • 0
この回答へのお礼

ありがとうございます♪

>「参照へのポインタ」とか「参照の配列」も作れません.
そのへんはOKです。たぶんw

>7 の場合 T = int& で T& という形ですから T& = int& です (この場合は T&& も int&).

あ、そうなるんですか。
それはどの部分に書いてありましたでしょうか?(あるいは基本的なことなんでしょうか)


>質問文のコードを合法にしないとまずいのは~

なるほど、基本的な使い方では問題はないということですね。

う~~んしかし

http://boost.cppll.jp/HEAD/libs/functional/binde …
(↑たぶん文字エンコーディングは 日本語(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行以内程度で再現できるコードが出来ればそれをみたいです)

お礼日時:2012/03/08 04:40

ざっと調べてみましたが, 11 で導入された可能性があります. 98 を斜め読みしてみたものの, 対応する文言は見つからず.



std::list については要素が CopyConstructible でなければならない (つまり「& でアドレスが取れる」必要がある) ので参照は無理じゃないかなぁ.

この回答への補足

おお
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なら通りました。

補足日時:2012/03/08 19:10
    • good
    • 0
この回答へのお礼

>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でなくても合法
というのは確定的なので

ふつーに自分でそんなにややこしくない使い方する分には
何ら問題なさ気、って判断しといていい、でしょうかね。

お礼日時:2012/03/08 18:41

あ, 今 C++11 の FDIS 見てたらなんとなく言いたいことがわかった気がする.



「参照を参照型の引数として関数に渡す」ことはまったく問題ありません... というか, それは否定されるといろいろめんどくさい.

「参照の参照」は作れません. つまり
int への参照
はできる (int& だ) が
「int への参照」の参照
は NG.
    • good
    • 0
この回答へのお礼

どうもありがとうございます♪

あ~、そんな感じの書かれ方だった気がします。
でもそんときも「コードを見なかったんで正確に分かんなかった」んですが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"("不可"ではなく)」
と言われるときの「参照の参照」とは
こういう風にテンプレートを使った時の、それに限る
という事で大丈夫でしょうかね?

お礼日時:2012/03/07 19:06

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!

このQ&Aを見た人はこんなQ&Aも見ています