なまもの備忘録

気になったことをつらつらと書いていきます

仮引数が同じメンバ関数のオーバーロード

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にするとうまくいく。