rep マクロでハマった

注意: この記事は C++ 初心者が書いたものです.

前置き

最近競プロで C++ を使い始めたのですが,まずは人の真似からということで便利マクロの代表格であろう rep マクロを導入していました.rep マクロにはいろいろなバリエーションがあると思いますが,私はこれを次のように define していました.

#define rep(i, n) for (int i = 0; i < (n); ++i)

 0\leq i \lt n を満たす整数  i を昇順に走査するマクロです.そして,この派生として  l\leq i\lt r を満たす整数  i を昇順に走査する replr なるマクロも定義しており,その定義は次のようなものでした.

#define replr(i, l, r) for (int i = (l); i < (r); ++i)

既に予想できた人もいるかもしれないんですが,i の型を int で固定していたことが原因でバグを埋め込んでしまいました.というのも,例えば lrlong long であり,int に収まらない値であった場合に意図通りの動作をしてくれません.

rep(i, n)nint に収まらないことなんてそうそうないのでまだいいんですが,replr の方は int に収まらないことがたまにあるので直します.

解決策

常に long long にしておけば良いのはそうなんですが,不必要に大きなサイズの型を使うのは個人的に嫌なので,定義を次のように変更してみました.

#define replr(i, l, r) for (decltype(r) i = 0; i < (r); ++i)

decltyper の型を取得しています.しかし,これではうまくいかないことがあります.例えば,次のような場合です.

#include <iostream>

constexpr int N = 10;

int main() {
    for (decltype(N) i = 0; i < N; ++i) {
        std::cout << i << std::endl;
    }
}

Nconst なので decltype(N) i = 0; の宣言で i の型は const int となり,++i の部分で値を変更しようとしているためコンパイルエラーとなります.

そこで,もう一工夫して次のように定義を変更してみます.

#include <type_traits>

#define replr(i, l, r) for (std::remove_const_t<decltype(r)> i = l; i < r; ++i) {

これでめでたく動きました.

おわりに

私は C++ のことを何も分かっていないので,「こうした方がいい」,「これはマズイ」みたいなことがあれば教えてください.