Variadic模板数组调用未定义的行为

时间:2016-05-29 16:51:15

标签: c++ arrays templates c++11 undefined-behavior

我正在尝试提供参数包来初始化数组。我认为它应该有效,因为:

  • 我正在使用sizeof...来获取参数包的大小
  • 这是一个模板,sizeof是一个编译时构造,因此应该在编译时知道,这意味着arr不应该是一个可变长度的数组
  • 我正确转发论据

然而,我得到垃圾作为输出和警告。首先是代码:

#include <iostream>
#include <utility>

template <typename... Args>
void foo(Args&&... args)
{
    int arr[sizeof...(Args)]{std::forward<Args>(args)()...};
    for (auto i = 0u; i < sizeof(arr); ++i)
        std::cout << arr[i];
}

int a() { return 1; }
int b() { return 2; }
int c() { return 3; }

int main()
{
    foo(a, b, c);  
}

然后警告和输出:

warning: iteration 3 invokes undefined behavior [-Waggressive-loop-optimizations]
         std::cout << arr[i];
         ~~~~~~~~~~^~~~~~~

note: within this loop
     for (auto i = 0u; i < sizeof(arr); ++i)
                       ~~^~~~~~~~~

1230-282327744327670000-133971368332712

任何人都可以看到我的错误吗?

2 个答案:

答案 0 :(得分:0)

template <typename... Args>
void foo(Args&&... args)
{
    // edit 1 : define the extent of the array once, reuse.
    constexpr std::size_t extent = sizeof...(Args);

    // edit 2 : in this case, no need to forward references since
    //          we're not actually forwarding them anywhere
    int arr[extent]{args()...};

    // edit 3 : 0 < i < extent of array
    for (std::size_t i = 0; i < extent ; ++i)
        std::cout << arr[i];
}

答案 1 :(得分:0)

正如@Casey的评论中所提到的,sizeof(arr)返回您为数组选择的类型变量的大小乘以数组本身的长度(在您的情况下,3 * sizeof(int)

要解决此问题,您可以使用以下方法迭代sizeof...(Args)

for (auto i = 0u; i < sizeof...(Args); ++i)

或者在填充数组时打印值,如下所示:

#include <iostream>
#include <utility>

template <typename... Args>
void foo(Args&&... args) {
    int arr[sizeof...(Args)] = {
        (std::cout << args(), std::forward<Args>(args)())...
    };
    (void)arr;
}

int a() { return 1; }
int b() { return 2; }
int c() { return 3; }

int main() {
    foo(a, b, c);
}

如果该功能的唯一目的是打印值,您也可以使用:

int arr[sizeof...(Args)] = { (std::cout << args(), 0)... };

请注意,上述代码并未考虑sizeof...(Args)等于0的情况 这样做确实非常微不足道,我将其排除在答案之外。