Clang 抱怨:"pointer is initialized by a temporary array"

Clang complains: "pointer is initialized by a temporary array"

我有一个不同长度的(指向)数组的数组,我了解到我可以使用复合文字来定义它:

const uint8_t *const minutes[] = {
    (const uint8_t[]) {END},
    (const uint8_t[]) {1, 2, 3, 4, 5 END},
    (const uint8_t[]) {8, 9, END},
    (const uint8_t[]) {10, 11, 12, END},
    ...
}; 

gcc 很好地接受了这个,但是 clang 说:pointer is initialized by a temporary array, which will be destroyed at the end of the full-expression。这是什么意思?代码 似乎 可以工作,但是话又说回来,许多东西 似乎 在指向不再分配的内存时可以工作。这是我需要担心的事情吗? (最终我真的只需要它与 gcc 一起工作。)

更新:发生了一些可疑的事情。它说 here 那:

Compound literals yield lvalues. This means that you can take the address of a compound literal, which is the address of the unnamed object declared by the compound literal. As long as the compound literal does not have a const-qualified type, you can use the pointer to modify it.

   `struct POINT *p;
   p = &(struct POINT) {1, 1};

这个示例代码似乎正在做我想做的事情:一个指向由复合文字定义的东西的指针。那么clang报错信息合法吗?当使用 clang 或 gcc 编译时,这最终会指向未分配的内存吗?

更新 2: 发现一些 documentation:“在 C 中,复合文字指定一个具有静态或自动存储持续时间的未命名对象。在 C++ 中,复合文字指定一个临时对象,它只存在到其结束时full-expression." 所以 clang 发出警告似乎是正确的,gcc 可能也应该这样做,但即使 -Wall -Wextra.

我无法猜出为什么从 C++ 中删除了一个有用的 C 特性,并且没有提供完成相同事情的优雅替代方法。

嗯,这意味着,这个表达式

(const uint8_t[]) {1, 2, 3, 4, 5 END},

创建一个临时对象——临时的,因为它没有任何名称可以持续到它所属的表达式之外——它在完整表达式的末尾被销毁,这意味着:

 };

定义"the full expression",此时所有临时对象都将被销毁,指针数组minutes保存指向destroyed 个对象,这就是编译器发出警告的原因。

希望对您有所帮助。

更新:感谢 deniss 指出原方案中的缺陷。

static constexpr uint8_t END = 0xff;

template<uint8_t...x>
const uint8_t* make()
{
    static const uint8_t _[] = { x..., END };
    return _;
}

const uint8_t* const array[] = {
    make<>(),
    make<1, 2, 3, 4, 5>(),
    make<8, 9>(),
    make<10, 11, 12>()
};

嗯,clang说的对,就这样吧:

namespace elements
{
    const uint8_t row1[] = {END};
    const uint8_t row2[] = {1, 2, 3, 4, 5, END};
    ...
}
const uint8_t *const minutes[] = {
    elements::row1,
    elements::row2,
    ...
}; 

你可以想到更多的 C++ 解决方案,比如使用 std::tuple:

#include <tuple>

constexpr auto minutes = std::make_tuple(
    std::make_tuple(), 
    std::make_tuple(1,2,3,4,5),
    std::make_tuple(8,9,10)); 

#include <iostream>
#include <type_traits>
int main() {
    std::cout << std::tuple_size<decltype(minutes)>::value << std::endl;
    std::cout << std::tuple_size<std::remove_reference_t<decltype(std::get<1>(minutes))>>::value << std::endl;
}