3

我看到了这段代码

template<typename F, typename ... Ts, std::size_t... Is>
void visit_tuple_impl(std::tuple<Ts...>& tup, F&&f, 
    std::index_sequence<Is...>, std::size_t idx) {
    auto check_call = [&](auto & elem, std::size_t I) {
        if (I == idx)
            f(elem);
    };

    (check_call(std::get<Is>(tup), Is), ...);
}

template<typename F, typename ... Ts>
void visit_tuple(std::tuple<Ts...>& tup, std::size_t idx, F&& f) {
    using TT = std::decay_t<decltype(tup)>;
    constexpr std::size_t size = std::tuple_size_v<TT>;
    auto idxs = std::make_index_sequence<size> {};
    visit_tuple_impl(tup, std::forward<F>(f), idxs, idx);
}

    void printComponents(uInt vectorId) {
        visit_tuple(data, vectorId, [&](auto &vec) {
            for(auto &elem : vec) {
                elem.print();
                std::cout << ' ';
            }
            std::cout << '\n';
        });
    }

用法是这样的:

int vecId = 1;
ecsData.printComponents(vecId);

这意味着给定一个运行时变量id,元组中的每个类型都可以在运行时在 for 循环中使用它来访问,而不是简单地使用std::get<2>()不接受运行时变量作为模板参数的类型。所以我的问题是“使用check_call()lambda的折叠表达式不是visit_tuple_impl每次被调用时都会检查元组中的每个 id 吗?引用答案:I wouldn't be surprised if it generates a lookup table that's O(1),但这似乎更像是一个猜测,所以我向你提出这个问题伙计们。

check_call()每次调用都检查元组中的每种类型吗visit_tuple_impl

4

1 回答 1

1

是的,这正是模板将扩展到的内容。这就是模板扩展的直接、字面结果,用这么多的话来说。但无论事实是否如此,这将是一个完全不同的故事。

允许编译器进行任何没有可观察结果的优化(前提是一切都在定义的行为范围内)。使用查找表(和一些边界检查)在这里没有可观察到的结果,因此您的编译器很可能会生成基于查找表的代码,如果它足够聪明,可以弄清楚这里发生了什么,并且它决定这样做。

了解您的编译器是否足够聪明以弄清楚这是怎么回事的唯一方法是尽可能地帮助您的编译器(例如,明确指定const std::size_t idx为函数的参数),然后查看生成的代码。

请记住,结果可能会因优化级别和元组大小而异。这非常类似于switch具有连续case值的普通文本,其中(通常)编译器仅在有几个值时生成一些比较,并在cases 的数量超过某个阈值时切换到查找表。

但是应该很明显,C++ 标准并不要求编译器执行所有可能的优化,这些优化没有可观察到的结果,但对人脑来说是显而易见的,并且编译器也不需要执行这种特定的优化。

简短的回答:看看你的编译器做了什么,这是唯一知道的方法。

于 2019-12-10T03:33:16.917 回答