6

配列パラメーターの長さを推測するテンプレート関数があるとします。

template <size_t S>
void join(const char d[], const char *(&arr)[S]) { }

このように呼び出すと、すべてうまくいきます。

const char *messages[] = {
    "OK",
    "Not OK",
    "File not found"
};
join("\n", messages);

しかし、次のように、空の配列で呼び出すと:

const char *messages[] = { };
join("\n", messages);

…コンパイルされません (clang 4.0 で):

targs.cpp:9:5: エラー: 'join' の呼び出しに一致する関数がありません
    join("\n", メッセージ);
    ^~~~
targs.cpp:4:6: 注: 候補テンプレートは無視されました: 置換失敗 [S = 0 で]
void join(const char d[], const char *(&arr)[S]) { }
     ^
1 エラーが発生しました。

C++ が長さ 0 の配列を好まないことに関係していると推測していますが、関数がテンプレートではなく、長さを別のパラメーターとして受け取る場合、メッセージを長さ 0 として宣言しても問題ありません。配列。

ここで何が起こっているのですか?良い回避策はありますか?


私の実際の使用例は、HTTP API エンドポイントが受け取るパラメーターを定義することであり、次のようになります。

const api_param_t params[] = {
    { API_TYPE_STRING, "foo" },
    { API_TYPE_UINT64, "bar" },
    { API_TYPE_STRING, "baz" }
}
const api_status_t status_codes[] = { … };
const api_middleware_t middleware[] = { … };

new api_endpoint("/foo", params, status_codes, middleware);

ほとんどのエンドポイントは少なくとも 1 つのパラメーターを取りますが、多くのエンドポイントは何も取りません。これは、実際、GCC と clang の両方が実装する拡張機能のようです (ただし、完全ではないように見えます)。いくつかの回避策を考えることができます:

  • api_endpointコンストラクターを特殊なケースの長さゼロの引数にオーバーロードします (ただし、長さゼロの各パラメーターをカバーするには、そのうちの 2 3が必要です)。これは、GCC/clang 拡張で問題ありません。

  • 配列の長さを推測しようとせず、別のパラメーターとして受け取ります (そして、長さ 0 の配列を引き続き使用します)。

  • これらのパラメータには、ベクトルのような高レベルのデータ構造を使用してください

  • 魔法の値を使用して「空」を示す

…しかし、誰かがより良いアイデアを持っているなら、ぜひ聞いてみたい

4

1 に答える 1

8

このコードはそもそも合法ではありません:

const char *messages[] = { };

コンパイラが生成するエラーと警告は次のとおりです。

main.cpp:6:26: warning: zero size arrays are an extension [-Wzero-length-array]
const char *messages[] = { };
                         ^
main.cpp:7:1: error: no matching function for call to 'join'
join("\n", messages);
^~~~
main:3:6: note: candidate template ignored: substitution failure [with S = 0]: zero-length arrays are not permitted in C++
void join(const char d[], const char *(&arr)[S]) { }
     ^                                       ~
1 warning and 1 error generated.

したがって、長さゼロの配列は実際にはまったく許可されません。コンパイラには、長さゼロの配列の拡張機能があるようですが、この特定のケースはカバーされていません。拡張機能は、言語全体で一貫して機能するように拡張機能に費やされる作業が少ないため、そのような場合があります。

回避策は、長さゼロの配列が必要な理由と、それを他の場所でどのように使用しているかによって異なります。1 つの回避策として、代わりに単一の要素配列を使用することがあります。


これが回避策です。拡張機能では、配列サイズをゼロとして推定することは許可されていないため、この推定を必要としないオーバーロードを追加します。

template <size_t S>
void join(const char d[], const char *(&arr)[S]) {
    std::cout << "array length > 0\n";
}

void join(const char d[], const char *(&arr)[0]) {
    std::cout << "extension, zero length array\n";
}

int main() {
    const char *messages[] = {
        "OK",
        "Not OK",
        "File not found"
    };
    join("\n", messages);

    const char *messages2[] = { };
    join("\n", messages2);
}

これは拡張機能を使用しており、移植可能なコードではないことに注意してください。特定の C++ 実装に縛られるのを避けるために、移植可能なコードを作成することをお勧めします。-Wzero-length-arrayビルドにフラグを追加すると、この拡張機能にどれだけ依存しているかがわかります。

于 2012-10-05T16:35:51.093 に答える