int();とint(i);とint(1);の違いについての謎


の続きです。

括弧ありとなしで同じかと思ったんですが、どうも渡す引数によって挙動が変わるようです。

とりあえず以下の例を。

int main () {
    int();
    int(i);
    int(100);
    
    return 0;
}

これらは全て問題なくコンパイルが通ります。一体どういう風に解釈されているのかちょっとわかりません。

iの場合は変数宣言になり、引数無しや数値の場合は一体どういう風に解釈されているのかわかりません。

ということで一旦クラスにしてコンストラクタがどのように呼ばれているのか検証してみました。

#include <iostream>
using namespace std;

class CSample {
public:
    CSample () { cout << "CSample()" << endl; }
    CSample (int) { cout << "CSample(int)" << endl; }
};

int main () {
    CSample();
    CSample(i);
    CSample(100);
    
    return 0;
}
$ main
CSample()
CSample()
CSample(int)

なんだか面白い結果になりました。

引数無しや数値を渡した場合でもちゃんとコンストラクタが呼ばれています。しかし変数を指定してないのでインスタンス化がされたとしても実際のオブジェクトがありません。

ということはもしかして戻り値にオブジェクトが帰るのかな?と思って試してみました。

#include <iostream>
using namespace std;

class CSample {
public:
    CSample () { cout << "CSample()" << endl; }
    CSample (int) { cout << "CSample(int)" << endl; }
    void func () { cout << "func()" << endl; }
};

int main () {
    
    CSample obj1 = CSample();
    obj1.func();
    
    CSample obj2 = CSample(100);
    obj1.func();
    
    return 0;
}
$ main
CSample()
func()
CSample(int)
func()

おおうまく動いた。つまりこれはクラス括弧付きで呼ぶ場合、引数に変数名以外のものを渡すとそのクラスをインスタンス化したオブジェクトを戻り値として返すということなんですかね。

ということはオブジェクトの値渡しになるため、コピーコンストラクタが呼ばれているか試してみました。

#include <iostream>
using namespace std;

class CSample {
public:
    CSample () { cout << "CSample()" << endl; }
    CSample (const CSample&) { cout << "CSample(const CSample&)" << endl; }
};

int main () {
    CSample obj = CSample();
    
    return 0;
}
$ main
CSample()

あれれ??

呼ばれて無い・・・。CSample()の戻り値として返されたオブジェクトをobjに代入しているわけだから値渡しなわけでコピーコンストラクタが呼ばれないはずがありません。

一応普通に定義したオブジェクトを渡してみたところ、

#include <iostream>
using namespace std;

class CSample {
public:
    CSample () { cout << "CSample()" << endl; }
    CSample (const CSample&) { cout << "CSample(const CSample&)" << endl; }
};

int main () {
    CSample obj1;
    CSample obj2 = obj1;
    
    return 0;
}
$ main
CSample()
CSample(const CSample&)

ちゃんとコピーコンストラクタが呼ばれています。

かなり頭がMAXに混乱してきました。

CSample obj = CSample();

と書いた場合に内部で最適化されてコピーが発生しないように動いてくれてるということなのでしょうか?それとも僕が何か壮絶な勘違いをしているだけなのでしょうか?

ちなみにコピーコンストラクタが呼ばれないのであればprivateにしてても問題ないのかなと思って

#include <iostream>
using namespace std;

class CSample {
public:
    CSample () { cout << "CSample()" << endl; }
private:
    CSample (const CSample&) { cout << "CSample(const CSample&)" << endl; }
};

int main () {
    CSample obj = CSample();
    
    return 0;
}

としたところ、見事にコンパイルエラーが発生しました。

$ cl /W4 /EHsc main.cpp
Microsoft(R) 32-bit C/C++ Optimizing Compiler Version 15.00.30729.01 for 80x86
Copyright (C) Microsoft Corporation.  All rights reserved.

main.cpp
main.cpp(12) : error C2248: 'CSample::CSample' : private メンバ (クラス 'CSample' で宣言されている) にアクセスできません
        main.cpp(8) : 'CSample::CSample' の宣言を確認してください。
        main.cpp(4) : 'CSample' の宣言を確認してください。

えーなんで。呼ばれて無いのにアクセスできませんって。呼ぶ予定は無いけどアクセスできるかどうかの判断は有効ってこと?謎が深まるばかりです。おそらく僕の何かひとつの小さな見落としからこんな疑問が出てるのだとは思いますが・・・。

とりあえず今読み進めているロベール本にこれらの疑問の答えが書かれていると良いのですが。

追記

コメント欄にて色々と教えて頂きました。ちゃんと時間をかけて理解していきたいと思います。