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: 错误:没有匹配的函数调用“加入”
    加入(“\n”,消息);
    ^~~~
targs.cpp:4:6: 注意:候选模板被忽略:替换失败 [with S = 0]
无效连接(常量字符 d[],常量字符 *(&arr)[S]) { }
     ^
产生 1 个错误。

我猜它与不喜欢零长度数组的 C++ 有关,但如果函数不是模板并将长度作为单独的参数,它不会抱怨我将消息声明为零长度大批。

这是怎么回事,有什么好的解决方法吗?


我的实际用例是定义 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);

大多数端点至少采用一个参数,但许多端点不采用。看起来这确实是 GCC 和 clang 都实现的扩展(但是,看起来并不完全……)。我可以想到一些解决方法:

  • 将构造函数重载api_endpoint到特殊情况的零长度参数(但我需要其中的 2 3个来覆盖每个零长度参数),GCC/clang 扩展可以使用。

  • 不要试图推断数组长度,将其作为单独的参数(并继续使用零长度数组)

  • 对这些参数使用更高级别的数据结构,例如向量

  • 使用魔法值来表示“空”

…但如果有人有更好的想法,我很想听听他们

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.

所以实际上根本不允许使用零长度数组。您的编译器似乎具有零长度数组的扩展,但是,它不涵盖这种特定情况。扩展有时就是这样,因为更少的工作进入扩展以使它们与整个语言一致。

解决方法取决于您为什么需要零长度数组以及如何在其他地方使用它。一种解决方法可能是使用单个元素数组。


这是一个解决方法。由于扩展不允许将数组大小推导为零,因此添加了一个不需要此推导的重载:

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);
}

You should keep in mind that this is using an extension and is not portable code. You may prefer to write portable code in order to avoid being locked into any particular C++ implementation. You can see how much you rely on this extension by adding the flag -Wzero-length-array to your builds.

于 2012-10-05T16:35:51.093 回答