C++ NULLポインタ経由のメンバ関数呼び出し

実際に試してみると死なない

実際に試したところでは特に死んだりはしないようです

struct cfoo {
	void	foo() const {
		printf( "foo\n" );
	}
};

static_cast<cfoo*>(0)->foo();

またこんな感じのも死なないようです

struct cfoo {
	cfoo*	find( const char*	name ) const {
		if( !this ) return NULL;
		cfoo*	result = NULL;
		children_t::const_iterator	p = m_children.find( name );
		if( p ){
			result = p->second;
		}
		return	result;
	}
	cfoo*	parent() const {
		if( !this ) return NULL;
		return	m_parent;
	}
};
cfoo* q = p->find( "aaa" )->parent();

この仕組みを使えば C++ でも継続モナド的に書ける。というわけではありません

C的には当たり前

C的に考えれば、非仮想関数で非staticなメンバ関数は第一引数が this であるだけの普通の関数なので、第一引数に NULL を渡すか有効なアドレスを渡すかで動作が変わるわけはありません
呼び出した先で渡したアドレスをデリファレンスしないのであれば死なないのは当たり前です

しかし未定義動作

しかし C++ の標準としては未定義動作です

  1. static_cast(0)->foo() と (*static_cast(0)).foo() は同義である
  2. *static_cast(0) は明らかに NULLポインタをデリファレンスしている
  3. NULLポインタのデリファレンスは未定義動作と定められている
  4. 従って、NULLポインタ経由のメンバ関数呼び出しは未定義動作である

呼び出し先で this をデリファレンスしなければ OK的な記述は見つかりませんでした

ちゃんと NULL チェックをしましょう

this の NULL チェックをしましょう。じゃないよ

製品となるプログラミングで重要なのは標準ではなく製品です
標準に従ったコードなんだから製品に問題があってもそれは処理系が悪いんだ。などという言い訳が通るはずはないのですから、最終的には標準よりも実際に出力されたコードの挙動の方が重要なのは当たり前です
しかしだからと言って死なないんだから何をしてもいいんだ。標準なんて関係ない。ということでもありません

死なないかどうかに関係なくポインタは使う前にちゃんと NULL チェックをしましょう

余談

こちらは静的な問題で、関数呼び出しは動的な問題ですが
これも C++ では未定義な動作です

const char *p = 0;
const char *q = &*p;

しかしこれは未定義動作ではありません

char a[10] = {};
const char *p = &a[10]; // &a[10-1] + 1 としなくてもよい
ptrdiff_t celt = p - a;

(上は NULLアクセスの問題。下はオーバーフローの問題)

参考:
http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#232