仮引数が同じメンバ関数のオーバーロード
std::arrayの中身を読んでいたところ、以下のようなコードに出くわした。
template<typename _Tp, std::size_t _Nm> struct array { ... iterator begin() noexcept { return iterator(data()); } const_iterator begin() const noexcept { return const_iterator(data()); } }
仮引数と名前まで同じ関数がオーバーロードされている。 違うのは戻り値と、関数宣言の最後のconst指定だけだ。 関数のオーバーロードは仮引数が違わなければできないものではなかっただろうか。
調べてみると以下のようなページを見つけた。 ja.stackoverflow.com
どうやら、クラスのメンバ関数は暗黙の仮引数としてthisを持つらしい。 そして、constメンバ関数の場合、暗黙の仮引数thisにconstがつく。 だからオーバーロードができるのだ。
では、何のためにこのオーバーロードをしているのだろう。 試しに以下のコードをコンパイルしてみる。コンパイラはgcc4.2.1だ。
#include <array> class testArray { public: std::array<int, 3> ar; std::array<int, 3>::iterator begin() { return ar.begin(); } }; int main(){ testArray A = {1,2,3}; auto Aitr = A.begin(); return 0; }
このコードは、当然のことだが問題なく動作する。 一方、testArrayをconst付きで宣言すると。
#include <array> class testArray { public: std::array<int, 3> ar; std::array<int, 3>::iterator begin() { return ar.begin(); } }; int main(){ const testArray B = {1,2,3}; auto Bitr = B.begin(); return 0; }
コンパイルすると
g++ -std=c++11 test.cpp test.cpp:18:17: error: member function 'begin' not viable: 'this' argument has type 'const testArray', but function is not marked const auto Bitr = B.begin(); ^ test.cpp:8:34: note: 'begin' declared here std::array<int, 3>::iterator begin() ^ 1 error generated.
のようになってしまう。 よく考えてみれば当然で、const付きのクラスのconst修飾されてないメンバ変数が返ってしまったら、後からその値を変えることができてしまう。 ちなみに、以下のようなコードであれば問題なく動作する。
#include <iostream> #include <array> class testArray { public: std::array<int, 3> ar; std::array<int, 3>::const_iterator begin() const { return ar.cbegin(); } }; int main(){ const testArray B = {1,2,3}; auto Bitr = B.begin(); return 0; }
ところで関数宣言を
const std::array<int, 3>::iterator begin() const { const std::array<int, 3>::iterator itr = ar.begin(); return itr; }
のようにして実行すると
g++ -std=c++11 test.cpp test.cpp:10:37: error: cannot initialize a variable of type 'const std::array<int, 3>::iterator' (aka 'int *const') with an rvalue of type 'const_iterator' (aka 'const int *') const std::array<int, 3>::iterator itr = ar.begin(); ^ ~~~~~~~~~~ 1 error generated.
とエラーを吐かれてしまう。
これは、const std::array<int, 3>::iterator
がint型のconst pointerになるのに対し、ar.begin()
の返り値はarray内でtypedefされているconst_iterator
のconst int型のpointerになってしまうからだ。こういう場合はstd::array<int, 3>::const_iterator
にするとうまくいく。