我如何在不升级到 8.x 的情况下解决 GCC 中的这个参数扩展错误?

How can I work around this parameter expansion bug in GCC without upgrading to 8.x?

考虑以下代码:

#include <iostream>
#include <utility>
#include <array>
#include <functional>
#include <ctime>

template <unsigned N> void foo() { std::cout << N << "(" << ") "; }
template<> void foo<2>() { std::cout << "TWO (" <<  ") "; }

struct Foo {
    template <unsigned N> void operator()(std::integral_constant<unsigned,N>) { foo<N>(); }
};

template <std::size_t Offset, std::size_t... Idx, typename F>
void visit(F f, std::index_sequence<Idx...>, std::size_t n) {
    static std::array<std::function<void()>, sizeof...(Idx)> funcs {{
        [&f](){f(std::integral_constant<unsigned,Idx+Offset>{});}...
    }};

    funcs[n - Offset]();
};

template <std::size_t Start, std::size_t End, typename F>
void visit(F f, std::size_t n) {
    visit<Start>(f, std::make_index_sequence<End-Start>{}, n);
};

int main() {
    auto t = time(nullptr);
    for(int i = 0; i < 10; i++) {
        visit<1, 10>(Foo{}, (t+i) % 10);
    }
}

这是有效的 C++14(实际上,如果您自己编写 std::index_sequence,那么 C++11 也是有效的)。但是 - 它不能用 g++ 6.x 和 7.x 编译;只有 g++ 8.x 可以正确编译它(看到这种情况 on GodBolt)。

出于组织原因,我可以要求使用高达 7.2 的 g++ 版本。有没有一种方法可以在保持语义的同时更改代码,让 g++ 7.x 编译它?

GCC 的错误是它不能打包扩展 lambda。所以不要打包展开 lambdas。

template<class F, std::size_t Idx>
void caller(F& f) { f(std:::integral_constant<unsigned, Idx>()); }

template <std::size_t Offset, std::size_t... Idx, typename F>
void visit(F f, std::index_sequence<Idx...>, std::size_t n) {
    using ptr_type = void (*)(F&);
    static constexpr ptr_type funcs[] = {&caller<F, Idx+Offset>...};
    funcs[n-Offset](f);
}

我一石打二鸟。而且 whaddaya 知道 - 这意味着将 lambda 移动到辅助函数中以避免不同的参数包相互干扰。作为@T.C。建议 - 这就是 GCC 遇到的问题,因此可以避免该问题。

template <std::size_t N, typename F, typename... Ts>
std::function<void(Ts...)> make_visitor(F f) {
    return 
        [&f](Ts... args) {
            f(std::integral_constant<std::size_t,N>{}, std::forward<Ts>(args)...);
        };
}

template <std::size_t Offset, std::size_t... Idx, typename F, typename... Ts>
void visit(F f, std::index_sequence<Idx...>, std::size_t n, Ts... args) {
    static std::array<std::function<void(Ts...)>, sizeof...(Idx)> funcs {{
        make_visitor<Idx+Offset, F, Ts...>(f)...
    }};
    funcs[n-Offset](std::forward<Ts>(args)...);
};

template <std::size_t Start, std::size_t End, typename F, typename... Ts>
void visit(F f, std::size_t n, Ts... args) {
    visit<Start>(f, std::make_index_sequence<End-Start>{}, n, std::forward<Ts>(args)...);
};