模板不会推断C ++中零长度数组的大小

时间:2012-10-05 16:28:26

标签: c++ templates

假设我有一个模板函数,它推断出数组参数的长度。

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: error: no matching function for call to 'join'
    join("\n", messages);
    ^~~~
targs.cpp:4:6: note: candidate template ignored: substitution failure [with S = 0]
void join(const char d[], const char *(&arr)[S]) { }
     ^
1 error generated.

我猜它与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扩展名可以。

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

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

  • 使用魔术值表示“空”

...但如果有人有更好的想法,我很乐意听到他们

1 个答案:

答案 0 :(得分:7)

此代码首先是不合法的:

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

您应该记住,这是使用扩展程序,而不是可移植代码。您可能更喜欢编写可移植代码,以避免被锁定到任何特定的C ++实现中。您可以通过向构建中添加标记-Wzero-length-array来了解您对此扩展的依赖程度。