とくにあぶなくないRiSKのブログ

危ないRiSKのブログだったかもしれない。本当はRiSKだけどググラビリティとか取得できるIDの都合でsscriskも使ったり。

boost::overloaded_functionを使ったオーバーロードって素晴らしいですよね!

オーバーロードって素晴らしいですよね! - ぐるぐる~

このエントリでは、C#において

オーバーロードがいらないは完全に言い過ぎですけど、オーバーロードを入れてしまったがために(後から導入した)関数との統一性がなくなってしまっています。

ということを主張し、F#ならいい感じですよとまとめています。ここでの「統一性」とは、できる/できないという点で、ということでしょう。

それC++ならできるよ

メンバ変数でオーバーロード

C#では

フィールドやプロパティはオーバーロード出来ないのですね・・・

とのこと。

フィールドやプロパティは、C++でメンバ変数(メンバオブジェクト)に対応します。言語仕様ではC#同様オーバーロードできないのですが、ライブラリを使えばメンバ変数でオーバーロードができちゃったりします。

#include<boost/functional/overloaded_function.hpp> 
#include<string>

struct hoge{
 const boost::overloaded_function<
  hoge(int),
  hoge(std::string)
 > something;
 hoge() :
  something(
   [](int n) -> hoge { std::cout << "int: " << n << '\n'; return {}; },
   [](std::string s) -> hoge { std::cout << "std::string: " << s << '\n'; return {}; }
  )
 {}
};

int main(){
 hoge h;
 h.something(42);
 h.something("Life");
}

ここで、h.someshingはメンバ変数です。somethingはラムダ(≒関数)で初期化されています。ご覧の通り、メンバ変数と関数の組み合わせでオーバーロードできました。

オーバーロードした関数を返す

C#では

引数を2つ取るメソッドはオーバーロード出来ます。

でも、同じようなことが出来る関数を返すメソッドはオーバーロード出来ません。

とのこと。

メソッドは、C++でメンバ関数に対応します。言語仕様ではC#同様返却値の型だけが異なるだけのオーバーロードはできないのですが、ライブラリを使えば疑似的に返却値の型だけが異なるだけのオーバーロードができちゃったりします。

#include<boost/functional/overloaded_function.hpp> 
#include<string>

struct hoge{
 boost::overloaded_function<
  hoge(int),
  hoge(std::string)
 > something(int a) const {
  return {
   [a](int b) -> hoge { std::cout << a << ", " << b << '\n'; return {}; },
   [a](std::string s) -> hoge { std::cout << a << ", " << s << '\n'; return {}; }
  };
 }
};

int main(){
 hoge h;
 h.something(0)(42);
 h.something(1)("Universe");
}

ここで、h.somethingはメンバ関数です。ご覧の通り、h.something(0)(42)では hoge (int)版の関数が返され、h.something(1)("Universe")ではhoge (std::string)版の関数が返されているかのようでしょう。

boost::overloaded_function

boost::overloaded_functionを使うと、関数っぽいものを突っ込んで、呼び出すときにオーバーロードされた適切な関数を呼んでくれます。オーバーロードをオブジェクトとして持ち歩くことができるのです。いわば遅延オーバーロードとして働くので、疑似的に返却値の型だけが異なるだけのオーバーロードができちゃったりします。

適切な箇所では迷わず使いましょう。

リンク

Chapter 1. Boost.Functional/OverloadedFunction