Как можно обойти эту ошибку расширения параметров в GCC без обновления до 8.x? - PullRequest
0 голосов
/ 07 ноября 2018

Рассмотрим следующий код:

#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 (фактически, также допустимый C ++ 11, если вы пишете свой собственный std::index_sequence). Но - он не компилируется с g ++ 6.x и 7.x; только g ++ 8.x компилирует его правильно (см. это на GodBolt ).

По организационным причинам я могу потребовать использовать версию g ++ до 7.2. Есть ли способ, которым я мог бы изменить код, сохраняя семантику, чтобы g ++ 7.x скомпилировал его?

Ответы [ 2 ]

0 голосов
/ 08 ноября 2018

Я ударил двух зайцев одним выстрелом и расширил этот механизм посещения до функций, принимающих произвольное количество аргументов . И что бы знать - это означало, что лямбда-функция была переведена в вспомогательную функцию, чтобы избежать мешающих друг другу пакетов параметров. Как @ 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)...);
};
0 голосов
/ 07 ноября 2018

Ошибка GCC в том, что он не может упаковать лямбды-расширения. Так что не упаковывайте лямбды.

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