Posted at

値渡しと参照渡しのオーバーロード

More than 3 years have passed since last update.


呼び出す側から値渡しと参照渡しは同じに見えるので多重定義は一見不可能に見えるが実は可能である。

void func (Foo f);   //#1

void func (Foo &f); //#2

int main()
{
Foo ff;
func(ff); //#1か#2どっちを呼び出しているかわからない

retun 0;
}


以下のようにすると可能らしい

//----------------------------------------------------------------

//
// 値渡しと参照渡しのオーバーロードについてのサンプルコード
//
//----------------------------------------------------------------

#include <iostream>

void func(int i)
{
std::cout << "void func(int)" << std::endl;
}

void func(int& i)
{
std::cout << "void func(int&)" << std::endl;
}

void func(const int& i)
{
std::cout << "void func(const int&)" << std::endl;
}

class test {
public:
void func(int i)
{
std::cout << "void test::func(int)" << std::endl;
}

void func(int& i)
{
std::cout << "void test::func(int&)" << std::endl;
}

void func(const int& i)
{
std::cout << "void test::func(const int&)" << std::endl;
}
};

int main(void)
{
int v = 0;
test obj;

{
//次のコードはコンパイルエラー
//std::cout << "関数:普通に呼び出し" << std::endl;
//func(v);
}

{
std::cout << "関数:キャストによる呼び出し" << std::endl;
(static_cast<void (*)(int) >(func))(v);
(static_cast<void (*)(int&) >(func))(v);
(static_cast<void (*)(const int&)>(func))(v);

//次のような記述もできる
//((static_cast<void (*)(int) >(&func)))(v);
//((static_cast<void (*)(int&) >(&func)))(v);
//((static_cast<void (*)(const int&)>(&func)))(v);

//次のような記述もできる
//(*(static_cast<void (*)(int) >(func)))(v);
//(*(static_cast<void (*)(int&) >(func)))(v);
//(*(static_cast<void (*)(const int&)>(func)))(v);

//次のような記述もできる
//(*(static_cast<void (*)(int) >(&func)))(v);
//(*(static_cast<void (*)(int&) >(&func)))(v);
//(*(static_cast<void (*)(const int&)>(&func)))(v);
}

{
std::cout << "関数:ポインタによる呼び出し" << std::endl;
typedef void (*typefuncval )(int);
typedef void (*typefuncref )(int&);
typedef void (*typefunccref)(const int&);
typefuncval pfuncval = func;
typefuncref pfuncref = func;
typefunccref pfunccref = func;
pfuncval (v);
pfuncref (v);
pfunccref(v);

//次のような記述もできる
//typefuncval pfuncval = &func;
//typefuncref pfuncref = &func;
//typefunccref pfunccref = &func;
//pfuncval (v);
//pfuncref (v);
//pfunccref(v);

//次のような記述もできる
//typefuncval pfuncval = func;
//typefuncref pfuncref = func;
//typefunccref pfunccref = func;
//(*pfuncval )(v);
//(*pfuncref )(v);
//(*pfunccref)(v);

//次のような記述もできる
//typefuncval pfuncval = &func;
//typefuncref pfuncref = &func;
//typefunccref pfunccref = &func;
//(*pfuncval )(v);
//(*pfuncref )(v);
//(*pfunccref)(v);
}

{
//次のコードはコンパイルエラー
//std::cout << "メンバ関数:普通に呼び出し" << std::endl;
//obj.func(v);
}

{
std::cout << "メンバ関数:キャストによる呼び出し" << std::endl;
(obj.*(static_cast<void (test::*)(int) >(&test::func)))(v);
(obj.*(static_cast<void (test::*)(int&) >(&test::func)))(v);
(obj.*(static_cast<void (test::*)(const int&)>(&test::func)))(v);
}

{
std::cout << "メンバ関数:ポインタによる呼び出し" << std::endl;
typedef void (test::*typefuncval )(int);
typedef void (test::*typefuncref )(int&);
typedef void (test::*typefunccref)(const int&);
typefuncval pfuncval = &test::func;
typefuncref pfuncref = &test::func;
typefunccref pfunccref = &test::func;
(obj.*pfuncval )(v);
(obj.*pfuncref )(v);
(obj.*pfunccref)(v);
}

return 0;
}

//----------------------------------------------------------------
// 【説明】
//
// 普通に呼び出せなくなるが
// オーバーロード自体は可能
//
// しかし 値渡し と 参照渡し のオーバーロードが
// 必要になることは たぶん ないでしょう...
//
//----------------------------------------------------------------

//----------------------------------------------------------------
// 【おまけ】
//
// 非const参照とconst参照のオーバーロードについてのサンプルコード
//
// http://ideone.com/qqG0ZK
//
//----------------------------------------------------------------

//----------------------------------------------------------------
// 【追記:2014/10/26 10:27】
//
// 値渡し と 参照渡し のオーバーロードは 単なる話のネタですが
// 関数を呼び分ける方法は 実際に使用することが あるかもしれません
//
// http://ideone.com/FGD4Zs
//
//----------------------------------------------------------------


const 非constのオーバーロード

//----------------------------------------------------------------

//
// 非const参照とconst参照のオーバーロードについてのサンプルコード
//
//----------------------------------------------------------------

#include <iostream>

void func(int& i)
{
std::cout << "void func(int& i)" << std::endl;
}

void func(const int& i)
{
std::cout << "void func(const int& i)" << std::endl;
}

void func(char* c)
{
std::cout << "void func(char* c)" << std::endl;
}

void func(const char* c)
{
std::cout << "void func(const char* c)" << std::endl;
}

int main(void)
{
{
int v = 0;
const int c = 0;
func(v);
func(c);
func(0);
}

{
char v[] = "";
const char c[] = "";
func(v);
func(c);
func("");
}

return 0;
}

//----------------------------------------------------------------
// 【説明】
//
// 非const参照 と const参照 の オーバーロード
// 非constポインタ と constポインタ の オーバーロード
// これらは 普通に呼び分けることができる
//
// 非constメンバ関数 と constメンバ関数 の呼び分けも
// これと 同じような感じで 呼び分けられるのでしょう...たぶん
//
// ちなみに 次のようなオーバーロードはできない(コンパイルエラーになる)
// void func(int i) と void func(const int i) の オーバーロード
// void func(char* c) と void func(char* const c) の オーバーロード
// void func(const char* c) と void func(const char* const c) の オーバーロード
//
//----------------------------------------------------------------

非コンスト=>コンスト

コンスト=>コンスト

の代入が可能なので const int aとint aはオーバーロードできない(呼び出しで区別できない)

ただしポインタ指す変数の中身がconstかどうかははんだんできるので

const int *a

int *a

は区別できる