型変換演算子を使って戻り値のオーバーロード風を実現

C++テンプレートテクニック 9-3


戻り値のみでは通常オーバーロードはできません。

#include <iostream>
using std::cout; using std::endl;

class CClassA {};
class CClassB {};

CClassA foo() { return CClassA(); }
CClassB foo() { return CClassB(); }

int main () {
    CClassA a = foo();
    CClassB b = foo();
    
    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(8) : error C2556: 'CClassB foo(void)' : オーバーロード関数は、'CClassA foo(void)' と戻り値の型のみが異なります。
        main.cpp(7) : 'foo' の宣言を確認してください。
main.cpp(8) : error C2371: 'foo' : 再定義されています。異なる基本型です。
        main.cpp(7) : 'foo' の宣言を確認してください。
main.cpp(11) : error C3861: 'foo': 識別子が見つかりませんでした
main.cpp(11) : error C2440: '初期化中' : 'CClassA (__cdecl *)(void)' から 'CClassA' に変換できません。
        コンストラクタはソース型を持てません、またはコンストラクタのオーバーロードの解決があいまいです。
main.cpp(12) : error C3861: 'foo': 識別子が見つかりませんでした
main.cpp(12) : error C2440: '初期化中' : 'CClassA (__cdecl *)(void)' から 'CClassB' に変換できません。
        コンストラクタはソース型を持てません、またはコンストラクタのオーバーロードの解決があいまいです。

このように、戻り値は省略可能なのでオーバーロードできません。

しかし、型変換演算子をうまく利用すればオーバーロードのようなことが可能となります。

#include <iostream>
using std::cout; using std::endl;

class CClassA {};
class CClassB {};

CClassA func_a() { return CClassA(); }
CClassB func_b() { return CClassB(); }

class X {
public:
    operator CClassA() { return func_a(); }
    operator CClassB() { return func_b(); }
};

X foo () {
    return X();
}

int main () {
    CClassA a = foo();
    CClassB b = foo();
    
    return 0;
}

func_aとfunc_bという戻り値の型が違う別々の関数を定義し、Xクラスの型変換演算子を利用すればオーバーロードが実現できます。

ただコード量が結構増えるのでもっと簡略化できれば良さそうなんですが。