C++のexplicitについての説明

このQ&Aのポイント
  • C++のキーワードであるexplicitは、クラスのコンストラクタに付けられるものであり、暗黙的な呼び出しを禁止するために使用されます。
  • クラスを関数の引数として使う際、その関数には、クラスのコンストラクタの引数になっているものも渡すことができてしまいます。
  • しかし、explicitキーワードを付けたコンストラクタは、必ず明示的に呼び出される必要があります。これにより、意図しない動作を防ぐことができます。
回答を見る
  • ベストアンサー

explicitの定義は?

C++においてexplicitについて調べると explicit <エクスプリシット>  「明示的」  引数をひとつだけ受け取るコンストラクタに付けることのできる C++ 言語のキーワード。  クラスを関数の引数として使う場合、その関数には、クラスのコンストラクタの引数になっているものも渡すことができてしまう。これは暗黙的にコンストラクタが呼び出されるからである。このとき、「コンストラクタの引数」がまるで「関数の引数」であるかのように振る舞ってしまい、本来ならコンパイルエラーとなって欲しい場面でも、見えない部分でコンストラクタが呼ばれることでコンパイルが通ってしまう。その結果、想定していない動作をする可能性がある。  そこで、コンストラクタには「暗示的に呼び出せない」ようにするためのキーワードがある。それが explicit である。このキーワードを付けたコンストラクタは、必ず明示的に呼び出される必要があり、前述のようなことをしようとするとコンパイル時にエラーが発生する。 となっていました 「クラスを関数の引数として使う場合、その関数には、クラスのコンストラクタの引数になっているものも渡すことができてしまう。」 のところが分かりません 具体例で説明してください 例えば template<class CharType,class Attr=char_traits<CharType>, Class Allocator=allocator<T> >class basic_string クラスのコンストラクタが explicit basic_string(const Allocator &a=Allocator()); ですがこのケースについて説明していただければ幸いです

  • nubou
  • お礼率62% (293/470)

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

  • ベストアンサー
回答No.4

他の方の意見がどうであれ、あなたが不要と思うなら使わなければいいだけのことではありませんか? # 僕もあなたも、言語設計者ではないのだから

nubou
質問者

お礼

できてしまったものは愚痴を言ってもしかたがないですね ありがとうございました

その他の回答 (3)

回答No.3

> f(3)はf(A(3))を意味するということですか? YES > これで何かまずい例はありますか? int から A に明示的に変換して欲しくないとき。 class A { public: A(int); }; class B { public: B(int); }; void f(const A&); void f(const B&); int main() { f(3); // エラー:どっちを呼ぶか決定できない return 0; }

nubou
質問者

補足

A a=b;//(1) が A a(b);//(2) を意味するのはaとbが厳密に同じA型であるときだけにしていればこのような混乱は発生しなかったのに柔軟性を求めすぎた結果ですね aとbが厳密に同じ型でなくても(1)と(2)が同じということにすることによるメリットはあるのでしょうか? basic_string str="moji"; などは basic_string str("moji"); としないといけないという風にしたからといってそれほど不便はないと思うのですが むしろexplicitなるキーワードを追加しなくてすみ混乱しなくなりバグも少なくなるような気がします なにか意見をいただければ幸いです

回答No.2

> explicit basic_string(const Allocator &a=Allocator()); > ですがこのケースについて説明していただければ幸いです explicitでないと、Allocatorからbasic_stringへの暗黙の変換が起こってしまうことになります。 std::allocator<char> a; std::string str = a; // できちゃう basic_stringの実装者はその変換を望まないのでexplicitにしたのでしょう。

回答No.1

class A { public: A(int); }; void f(const A&); int main() { f(3); // non-explicitであるがためにintからAへの変換が起こる return 0; }

nubou
質問者

補足

f(3)はf(A(3))を意味するということですか? これで何かまずい例はありますか? よろしくお願いします

関連するQ&A

  • じゃヴぁ

    class Demo{   String title;   public Demo(){}   public Demo(String t){title=t;}   public void showTitle(){System.out.println("Title is" + title);} } class DerivedDemo extends Demo{   public void setTitle(String tt){title = tt;} } class DerivedDemoApp {   public static void main(String args[]){     DerivedDemo dd = new DerivedDemo();     dd.showTitle();   } } このようなプログラムなんですが、 このDemoの引数なしコンストラクタを削除するとコンパイルエラーになります。 で、なぜエラーになるかという説明なのですが、テキストによると 「引数なしのコンストラクタが無い場合、super()が挿入される(空のコンストラクタという意味だと思います)Demoクラスを拡張しているDerivedDemoクラスにもコンストラクタが無いためこちらもsuper()を挿入します。しかしここでコンストラクタの連鎖の観点からDerivedDemoクラスのスーパークラスであるDemoクラスには引数の無いコンストラクタが定義されていないためコンパイルエラーとなる」 となっています。 しかし、ここで引数ありのコンストラクタをコメントアウトしてみると・・・なんとコンパイルは通ります。 すると、この説明の「連鎖してるから、コンストラクタの定義がいる」という説明はちょっと違うような気がします。 (1)引数なしのコンストラクタが定義されていない場合、なぜコンパイルエラーになるのでしょう? (2)このテキストの説明は正しいですか?? よろしくお願いします

    • ベストアンサー
    • Java
  • java コンパイルエラー

    java コンパイルエラー java 初心者です。 簡単なプログラムを作りながら練習していたのですが、Exceptionクラスを継承して新しい例外を作る以下のプログラムで、コンパイルエラーとなり、解決できません。 class NewException extends Exception{ public NewException(){ super("エラー"); } } これを javac でコンパイルすると、 NewException.java:3: シンボルを見つけられません。 シンボル: コンストラクタ Exception(java.lang.String) 場所  : Exception の クラス super("エラー"); ^ エラー1個 と言われます。 これを読むと「Exception には String を引数にとるコンストラクタはないよ」と言われているように思えるのですが、そんなはずないのでは。。 もし原因がお分かりの方がいらしたら、ご教示いただけるとありがたいです。

    • ベストアンサー
    • Java
  • リリースモードの時にリンカエラーが発生します

    リリースモードの時にリンカエラーが発生します Visual Studioで、自作ライブラリを使ったプログラムの開発をしていたのですが、リリースモード時にリンカエラーが発生してしまいました。 デバッグモード時は特に問題なくリンクできるのですが、リリースモードでコンパイルをすると以下のようなエラーが発生します。 LibGame.lib(Game.obj) : error LNK2001: 外部シンボル ""bool __cdecl FileExists(class std::basic_string,class std::allocator >)" (?FileExists@@YA_NV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z)" は未解決です。 LibGame.lib(Mesh.obj) : error LNK2001: 外部シンボル ""public: struct ARCHIVE_ENTRY * __thiscall CArchive::Find(class std::basic_string,class std::allocator >)" (?Find@CArchive@@QAEPAUARCHIVE_ENTRY@@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z)" は未解決です。 LibGame.lib(Mesh.obj) : error LNK2001: 外部シンボル ""class std::basic_string,class std::allocator > __cdecl ExtractFilePath(class std::basic_string,class std::allocator >)" (?ExtractFilePath@@YA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@V12@@Z)" は未解決です。 Release/ShtGame.exe : fatal error LNK1120: 外部参照 3 が未解決です。 プロジェクトのプロパティ等でリンク先のライブラリを有無やヘッダをチェックしたのですが、原因がつかめませんでした。 どのようなエラー原因が考えられるでしょうか? よろしくお願いします OS:Windows XP SP3 言語:VC++ 開発環境:Visual Studio 2005 Academic Edition

  • マネージクラスについて

    マネージクラスで可変長の引数を使いたいのですが、 public ref class CTest { public: CTest(); // コンストラクタ ~CTest(); // デフォルトコンストラクタ !CTest(); // ファイナライザ Msg(PSTR str, ...); // 可変長引数 }; とすると、 「error C3269: マネージ クラスのメンバ関数は '...' と共に宣言することはできません」 というエラーがでてしまいます。 なんとかしてマネージクラスで 可変長の引数を使いたいのですが、方法がわかりません。分かる方いますでしょうか?

  • 継承における暗黙のデフォルトコンストラクター

    ”継承の際、スーパークラスで引数なしのコンストラクターが省略せずにきちんと記述されるなら、そのサブクラスでコンストラクターを記述しなくても、スーパークラスの引数なしのコンストラクターが受け継がれるためコンパイルエラーがおきない。”のは分かりますが”スーパークラスで引数なしのコンストラクターが省略され、かつサブクラスでコンストラクターを省略した場合”はなぜコンパイルエラーがおきるのでしょうか? 暗黙の内にスーパークラスにコンストラクターが生成され、それがサブクラスに受け継がれることは出来ないのでしょうか? 宜しく願います。

  • テンプレート引数の型推測

    コンパイラはVC++2008です。 いろいろあって、あるクラスにおいて関数ポインタと関数オブジェクト双方を 同じように利用できないかと考えて、次のように試みました。 class Base { public:     virtual void func() =0; }; template<class Func> class CFunc :public Base { private:     Func m_func; public:     CThreadFunc(Func func):m_func(func){}     void func(){m_func();} }; class Hoge { private:     Base* base; public:     template<class Func>     Hoge(Func func)       :base(new CFunc<Func>(func))     {}     ~Hoge()     {       delete base;     }     void DoSomething()     {       base->func();     } }; クラスをテンプレートにするといちいち指定しなければならないので、 まず基底クラスに適当な仮想関数を設け、それを継承したクラスをテンプレートにしました。 そしてコンストラクタの引数で何かしらを受け取って、オーバーライドした関数の中で 関数ポインタか関数オブジェクトだと仮定して呼び出しています。 さらに基底クラスのポインタを目的のクラスが保持してやり、 こちらはコンストラクタをテンプレートにすることで引数から型を推測してもらうことで 先ほどのテンプレートクラスのインスタンスを作成しています。 そしてポインタを介してfunc()を使ったり…、などすれば、 とりあえず引数なしの関数と関数オブジェクトを同等に扱えないかなと思ったからです。 で、このようなクラスを作成してコンパイルすると、 void func(); //何かしら処理する関数 class Function { public:   void operator ()();  //何かしら処理する関数オブジェクト }; があったとして、 int main() {   Function function;   Hoge hoge(function); //いったん作ってから渡す   Hoge hoge2(func); //関数を渡す     hoge.DoSomething();   hoge2.DoSomething();    } は動きました。しかし、 int main() {   Hoge hoge(Function()); //引数を初期化する } とすると次のようなエラーが出ます。 warning C4930: 'Hoge hoge(Function(__cdecl *)(void))': プロトタイプされている関数が呼び出されませんでした (変数の定義が意図されていますか?) また、 int main() {   Hoge hoge(Function()); //引数を初期化する   hoge.DoSomething();  //クラスにアクセス } とすると次のようなほかのエラーが出ます。 error C2228: '.DoSomething' の左側はクラス、構造体、共用体でなければなりません。 しかし、例えば関数オブジェクトのコンストラクタに引数が設定されていたとして、 class Function { public:   Function(int dummy);  //何か値を受け取る   void operator ()();  //何かしら処理する関数オブジェクト }; となっていた時、 int main() {   Hoge hoge(Function(1)); //引数を初期化する   hoge.DoSomething();  //クラスにアクセス } の呼び出しは正常にコンパイルされ、想定通りの動きをします。 全く使わなくても、一つ以上の適当な引数を何でもいいからコンストラクタが持てば、 普通にコンパイルされるみたいです。ただ、デフォルト引数を与えてHoge hoge(Function())と 同じ形ですと引数があってもできないみたいです。 まったく通らないなら最初からあきらめるですが、中途半端にちゃんと動くために エラーの原因を知りたいと思っています。 テンプレートの場合には、引数に渡すタイミングで初期化はしてはいけないのでしょうか?

  • C++の規格

    デフォルト引数は、関数の宣言部、または定義部の いずれかで与えることができると思うのですが、 VC6sp6で +-------------------------------+ class tmp{ int m_x; public: tmp(int x); }; tmp::tmp(int x=0) : m_x(x){} +-------------------------------+ とすると、「デフォルトコンストラクタがない」と、エラーになります。 (因みにBCC551ではコンパイルできます) また、デフォルトコンストラクタを追加すると、 「オーバーロード関数の呼び出しが解決できない」というエラーになります。 前者のエラーの原因は、コンパイラ エラー C2512を読めば理解できますし、 後者の言うことも納得できます。 ここで疑問です。 (1) デフォルト引数はどこで与えることができるか? (2) デフォルトコンストラクタは、ユーザ定義コンストラクタがある場合は、 必ず用意しなければならないか?(エラーc2512) C++の規格ではどうなっているのでしょうか? よろしくお願いします。

  • Javaのコンストラクタについて教えてください

    Javaを勉強している初心者です。 次のようなプログラムがあります。 このプログラムでclass aおよびclass bのデフォルトコンストラクタ a() {}とb() {}をコーディングしていないとコンパイルエラーになります。 b() {}についてはclass bのパラメータのあるコンストラクタb(String s)がサブクラスclass cから明示的に呼ばれていないのでデフォルトコンストラクタb() {}をコーディングしないとエラーになる…と考えればよいのでしょうか。 それでは、a() {}はなぜ必要なのでしょうか。 どなたか教えてください。 class a { a() {} a(String s) { System.out.println("In a's constructor..."); System.out.println(s); } } class b extends a { b() {} b(String s) { super(s); System.out.println("In b's constructor..."); System.out.println(s); } } class c extends b { c(String s) { System.out.println("In c's constructor..."); System.out.println(s); } public void some() { System.out.println("something..."); } } public class appJ01 { public static void main(String args[]) { c obj = new c("Hello from Java!"); } } 

  • スーパークラスのコンストラクタの呼び出し

    こんにちは。 スーパークラスに引数のあるコンストラクタと引数のないコンストラクタをサブクラスで継承する際の質問です。 具体的に書くと下記になります。 class A { A () {} A (int i) {} } class B extends A { } 何がわからないかというと 『スーパークラスのA()は、サブクラスのBでわざわざ明示的にスーパークラスのコンストラクタ呼び出しをしなくても問題ないというのはわかるのですが、なぜスーパークラスのA(int)は、呼び出さなくても良いのかということです。』 もしかしたら基礎中の基礎かもしれませんが、ご教授よろしくお願いいたします。

    • ベストアンサー
    • Java
  • ヘッダーファイルでは、他のヘッダーファイルをインクルードできないのか

    Javaを仕事で使っています。 最近趣味でC++を始めました。色々形式の違いに戸惑っています。 C++では関数を宣言しなければならないので、クラス名と同じヘッダーファイルにそのクラスで使う関数を宣言して、それをインクルードしています。 そこで今、壁にぶちあたりました。 あるクラス(仮にFooとします)の関数で、他のクラス型(Hogeとします)を引数に取りたいので、ヘッダーファイルにそれを宣言しようとしているのですが、コンパイルエラーになってしまいます。 Foo.hは以下のような感じ。 #include Hoge.h 中略 GetHoge(Hoge hoge); コンパイルエラーでは error C2011: 'Hoge' : 'class' 型の再定義 などと言われます。 クラスとその同名のヘッダーファイル、という形式を変えずに 他のクラス型を引数にとる関数を作るには、どうしたらよいのでしょうか? C++に関しては全くの素人です。詳しい方、ご教示願います。