2つの驚き?

アバター
tk-xleader
記事: 158
登録日時: 14年前
連絡を取る:

2つの驚き?

投稿記事 by tk-xleader » 14年前

この質問に回答するために、ちょいと2つほど引っかかったことがあります。
1つ目、たとえば配列の各要素にアクセスするコードってこう書くことがありますよね?

CODE:

for(int i; i 

namespace NS1{
	struct A{};
	template
	void func(A){
		std::cout
	void func(int){
		std::cout(a); //これは関数が見つからずエラー
	NS1::func(a);  //これは当然問題ない。
	NS2::func(10); //これも問題ない。

	//ここで、using宣言を用いて… (※)
	using NS2::func;

	func(a); //さっきは通らなかったのに…通る。もちろんNS1::funcが選ばれる。
	NS1::func(a);  //これは当然問題ない。
	func(10); //これは問題ない。
}
※の前後で何が変わったのか?このトリックは?

{※の前}
func(a) をコンパイラが解釈するときに、funcが不可視(少なくともmainから見える位置にはfuncという識別子はない)ため、どう解釈すべきかが曖昧なためコンパイルエラー
※曖昧 … func(a)というのは、テンプレート関数の呼び出しほかに、(func (a)という比較演算とも解釈できるため、funcが何者かが判別できないとこれを解釈できない。
{※の後}
func(a) をコンパイラが解釈するとき、using宣言によってNS2::func(これはテンプレート関数)が見えるため、これを関数呼び出しと解釈し、呼び出し関数の解決に走る。そこで、実引数依存の名前探索(ADL) - Wikipediaが行われ、NS1::funcが呼び出し関数の候補に加えられる。その結果、func(a)はNS1::funcの呼び出しへと解決される。

これは驚きです。using宣言でこうも変わるものか…
最後に編集したユーザー tk-xleader on 2011年8月18日(木) 23:56 [ 編集 2 回目 ]

アバター
GRAM
記事: 164
登録日時: 14年前

Re: 2つの驚き?

投稿記事 by GRAM » 14年前

関数テンプレートが話をややこしくしてますねw
自分も後者のコンパイルが通るのはびっくりしました。

アバター
GRAM
記事: 164
登録日時: 14年前

RE: 2つの驚き?

投稿記事 by GRAM » 14年前

usingを使っていればまだデバグは楽そうですけど
一番被害をこうむるのはグローバル空間を汚しちゃったり、using namespaceをやたらめったら使う人ですかね?

CODE:

#include 

 //-----ここはヘッダファイルにでもある-------
template 
void Func(int){
        std::cout 
        void Func( Empty ){
                std::cout (a); //これがコンパイル通るかどうかはヘッダの内容による。
}
まぁ関数を名前空間に入れないようなことはしないですが・・・using namespaceとかなら引っかかりそうな気もする。
しかしどうなんでしょう?気を付けて書いていればそこまで気にならないような気もしないではないです・・・

アバター
tk-xleader
記事: 158
登録日時: 14年前
連絡を取る:

Re: 2つの驚き?

投稿記事 by tk-xleader » 14年前

僕はグローバル空間でのusing宣言/指令はほとんどしないです。やるとしたら、ごく短い実験用のサンプルプログラムか、C#でのSystem以下の名前空間にアクセスする場合ですね(C#にはADLはありませんから)。
Func(a)が通るかどうかは、これが関数呼び出しと解釈できるかどうかによります。一つでもFunc(/*...*/)のような関数が可視空間に存在する場合、関数呼び出しと解釈されて、ADLでNS::Funcが見つかってしまいます。

この現象?問題?は2つのライブラリを併存させたときにはまりそうな問題です。