C++と色々

主にC++やプログラムに関する記事を投稿します。

コンストラクタを書いたつもりが関数宣言とみなされる

コンストラクタを書いたつもりが関数宣言とみなされる時があります。
これは俗に、「C++で最も奇妙な解析」と言われています。
以下の様な時です。

struct foo{};

struct hoge
{
    //引数を1つ取るコンストラクタ
    hoge(foo f) : f_(f)
    {
    }

    void func(){}

    foo f_;
};

int main()
{
    hoge h(foo());//関数宣言!
    //h.func();//エラー
}

hoge h(foo())は、戻り値がhoge、引数foo (*)()型を取る関数hの宣言とみなされます。関数hの引数は関数ポインタですね。
以下の4つは同じシグネチャの関数宣言です。*1

void func(void (*fp)());
void func(void fp());
void func(void (*)());
void func(void ());

比べてみればhoge h(foo());が関数宣言と解釈されるのが分かると思います。
では、どうすればこのコードを書いた人が意図したように解釈してくれるのでしょうか?

2つ解決する方法があります。1つ目はC++11の機能であるUniform Initializationを使う方法です。

struct foo{};

struct hoge
{
    //引数を1つ取るコンストラクタ
    hoge(foo f) : f_(f)
    {
    }

    void func(){}

    foo f_;
};

int main()
{
    hoge h{foo()};//変数宣言
    h.func();//OK
}

2つ目はC++03でも使える方法で、引数を括弧で囲む方法です。

struct foo{};

struct hoge
{
    //引数を1つ取るコンストラクタ
    hoge(foo f) : f_(f)
    {
    }

    void func(){}

    foo f_;
};

int main()
{
    hoge h((foo()));//変数宣言
    h.func();//OK
}

*1:下2つは仮引数名を省略しているので引数の値を関数内で使用することができませんが…