link もっと前
   2010年 6月 16日 -
      2010年 6月 16日  
link もっと後

link 未来から過去へ表示(*)
link 過去から未来へ表示

日々

link permalink

noncopyable を継承したクラスを継承するとエラー?

(説明は後述)link コードと VC++ のプロジェクトファイルを固めたものへのリンク。

C++ の超有名ライブラリ boost に、デフォルトのコピー操作をコンパイル時点でエラーにしてくれる boost::noncopyable という便利クラスがあります。

C++ ではコピー操作(コピーコンストラクタや operator=)が未定義のクラスでコピー操作を行うと、デフォルトのコピーが働きます。しかしポインタをコピーしてしまうなど、デフォルトのコピーでは困るケースがあります。boost::noncopyable を使うと、気づかないうちに意図しないコピーを使っていないかどうかを、コンパイル段階で検出できるのです。便利でしょ。

前置きが長くなりましたが、私もこれはいいなと思って使ってみたのですが、g++ 4.3.2 では何も言われないのに、VC++ 2010 だとエラーになってしまい困っています。

私の使い方が何か間違っているだけかもしれませんが、同じコードをコンパイルしても g++ 4.3.2 は何も言いません。しかも VC++ 2010 の出すエラーメッセージが意味不明すぎてどこをどう直せばいいのかわからんのです…。

やりたいこと

コピー OK なクラス A と、コピー NG な(Uncopyable(※1)を継承)クラス B を多重継承した、クラス C を定義(※2)します。このオブジェクトを例外で投げる(main() の throw C(); の部分)ということがしたいのです。

その際 g++ 4.3.2 は何も言わないし、できた実行ファイルも正常に動作もしますが、VC++ 2010 は「そんなことはできねーよ!」とお怒りになり、コンパイルエラーにされてしまいます。

パターン 1(g++ は OK、VC++ はエラー)

A <-----------------+-- C
Uncopyable <-- B <--'

しかし、クラス B を取っ払って、下記のように多重継承したクラス D を使うと VC++ も文句を言わなくなります。

パターン 2(g++ は OK、VC++ も OK)

A <-----------------+-- D
Uncopyable <--------'

(※)Uncopyable クラスは、boost::noncopyable のコピペにデバッグメッセージを追加しただけのクラスです。

(※2)コピー OK とコピー NG を多重継承するなんて、コピー OK なのか NG なのか矛盾してますよ、テメーの設計はおかしいですよ。という突っ込みはその通りなのですが、今は設計の話ではないので無視して進めてます。

コード

コードは下記の通りです。先頭の #define USE_CLASS_C を有効にするとパターン 1、コメントアウトなどして無効にするとパターン 2 となります。

g++ では通るけど VC++ 2010 だとエラー

#include <iostream>
#include <cstdio>

//#define USE_CLASS_C

class Uncopyable {
protected:
	Uncopyable() {
		std::cout << "Uncopyable::Uncopyable()" << std::endl;
	}
	~Uncopyable() throw() {
		std::cout << "Uncopyable::~Uncopyable()" << std::endl;
	}
	
private:
	Uncopyable(const Uncopyable&);
	const Uncopyable& operator=(const Uncopyable&);
	
};

class A {
public:
	A() {
		std::cout << "A::A()" << std::endl;
	}
	virtual ~A() {
		std::cout << "A::~A()" << std::endl;
	}
	
	virtual const char *func() {
		return "A";
	}
};

class B : private Uncopyable {
public:
	B() {
		std::cout << "B::B()" << std::endl;
	}
	virtual ~B() {
		std::cout << "B::~B()" << std::endl;
	}
};

#ifdef USE_CLASS_C
class C : public A, public B {
public:
	C() : A(), B() {
		std::cout << "C::C()" << std::endl;
	}
	virtual ~C() {
		std::cout << "C::~C()" << std::endl;
	}
	C(const C& c) : A(), B() {
		std::cout << "C::C(const C& c)" << std::endl;
	}
	
	virtual const char *func() {
		std::cout << "C::func()" << std::endl;
		return "C";
	}
};
#else
class D : public A, private Uncopyable {
public:
	D() : A(), Uncopyable() {
		std::cout << "D::D()" << std::endl;
	}
	virtual ~D() {
		std::cout << "D::~D()" << std::endl;
	}
	D(const D& d) : A(), Uncopyable() {
		std::cout << "D::D(const D&)" << std::endl;
	}
	
	virtual const char *func() {
		std::cout << "D::func()" << std::endl;
		return "D";
	}
};
#endif

int main(int argc, char *argv[])
{
	try {
		std::cout << "try0" << std::endl;
#ifdef USE_CLASS_C
		throw C();
#else
		throw D();
#endif
	} catch (A& e) {
		std::cout << "catch0" << std::endl;
		std::cout << "---- " << e.func() << std::endl;
	}

	getchar();
}

コピペするのすら面倒くさい方のために、link コードと VC++ のプロジェクトファイルを固めたものを置きます。全コードの著作権は放棄しますが、Uncopyable クラスは boost ライブラリ boost::noncopyable の派生物です。

VC++ のエラーは意味がわかりません

コンパイルと実行結果は下記の通りです。

パターン 1 VC++(エラー)、g++(OK)、パターン 2 VC++(OK)、g++(OK)の順に、コンパイル結果と、実行結果を連続して掲載しています。

まずは Uncopyable を直接継承していないパターン(パターン 1)です。VC++ の結果です。

VC++ 2010、パターン 1(エラー)
1>------ ビルド開始: プロジェクト: noncopyable, 構成: Debug Win32 ------
1>  a.cpp
1>y:\projects\c\test\test_noncopyable\a.cpp(55): error C2248: 'Uncopyable::Uncopyable' : private メンバー (クラス 'Uncopyable' で宣言されている) にアクセスできません。
1>          y:projectsctesttest_noncopyablea.cpp(17) : 'Uncopyable::Uncopyable' の宣言を確認してください。
1>          y:projectsctesttest_noncopyablea.cpp(7) : 'Uncopyable' の宣言を確認してください。
1>          コンパイラでのこの診断により関数 'B::B(const B &)' が生成されました。
========== ビルド: 0 正常終了、1 失敗、0 更新不要、0 スキップ ==========

次に g++ の結果です。

g++ 4.3.2、パターン 1(OK)
$ g++ -Wall -DUSE_CLASS_C a.c -o c.out
$ ./c.out
try0
A::A()
Uncopyable::Uncopyable()
B::B()
C::C()
catch0
C::func()
---- C
C::~C()
B::~B()
Uncopyable::~Uncopyable()
A::~A()

次、クラス B を継承ツリーから省いて Uncopyable を直接継承しているパターン(パターン 2)です。VC++ の結果です。

VC++ 2010、パターン 2(OK)
try0
A::A()
Uncopyable::Uncopyable()
D::D()
catch0
D::func()
---- D
A::a = 0
D::~D()
Uncopyable::~Uncopyable()
A::~A()

次に g++ の結果です。

g++ 4.3.2、パターン 2(OK)
$ g++ -Wall a.c -o d.out
$ ./d.out
try0
A::A()
Uncopyable::Uncopyable()
D::D()
catch0
D::func()
---- D
D::~D()
Uncopyable::~Uncopyable()
A::~A()

な、なぜだ…。一体、何が間違っているというのだ…。

[編集者: すずき]
[更新: 2014年 9月 19日 01:21]
link 編集する

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



link もっと前
   2010年 6月 16日 -
      2010年 6月 16日  
link もっと後

管理用メニュー

link 記事を新規作成

合計:  counter total
本日:  counter today

link About www.katsuster.net
RDF ファイル RSS 1.0
QR コード QR コード

最終更新: 11/14 02:08

カレンダー

<2010>
<<<06>>>
--12345
6789101112
13141516171819
20212223242526
27282930---

最近のコメント 5件

  • link 18年10月12日
    すずき 「なるほど!\n京急、京成はヤバそうですね...」
    (更新:10/15 23:02)
  • link 18年10月12日
    ちかふみ 「閉会式直後の出国ラッシュ対策のためだそう...」
    (更新:10/15 20:43)
  • link 18年10月12日
    すずき 「あー、なるほど!閉会式の次にくっつけたん...」
    (更新:10/14 15:44)
  • link 18年10月12日
    hdk 「2020年の東京オリンピックが8月9日ま...」
    (更新:10/14 10:45)
  • link 18年09月07日
    すずき 「ありがとう!\nこちらこそ、楽しみにして...」
    (更新:09/11 19:30)

最近の記事 3件

link もっとみる
  • link 18年11月13日
    すずき 「[お気に入りのマンガ] Kindle Fire HD は大量の本を...」
    (更新:11/14 02:08)
  • link 18年11月10日
    すずき 「[ROCK64 の I2S が動かない] 先日(2018年 7月 ...」
    (更新:11/14 01:53)
  • link 18年11月11日
    すずき 「[linux-next で動かない ROCK64 の I2S] 昨...」
    (更新:11/14 01:52)

こんてんつ

open/close wiki
open/close Java API

過去の日記

open/close 2002年
open/close 2003年
open/close 2004年
open/close 2005年
open/close 2006年
open/close 2007年
open/close 2008年
open/close 2009年
open/close 2010年
open/close 2011年
open/close 2012年
open/close 2013年
open/close 2014年
open/close 2015年
open/close 2016年
open/close 2017年
open/close 2018年
open/close 過去日記について

その他の情報

open/close アクセス統計
open/close サーバ一覧
open/close サイトの情報