2017-04-12 16 views
11

Khi thử nghiệm với các cách thuận tiện để truy cập bộ dữ liệu dưới dạng vùng chứa, tôi đã viết một chương trình thử nghiệm.Trình biên dịch nào, nếu có lỗi trong việc mở rộng gói tham số?

trên kêu vang (3.9.1, và kêu vang táo) nó biên dịch như mong đợi, sản xuất sản lượng dự kiến:

1.1 
foo 
2 

trên gcc (5.4, 6.3), nó thất bại trong việc biên dịch:

<source>: In lambda function: 
<source>:14:61: error: parameter packs not expanded with '...': 
      +[](F& f, Tuple& tuple) { f(std::get<Is>(tuple)); }... 
                  ^
<source>:14:61: note:   'Is' 
<source>: In function 'decltype(auto) notstd::make_callers_impl(std::index_sequence<Is ...>)': 
<source>:14:64: error: expansion pattern '+<lambda>' contains no argument packs 
      +[](F& f, Tuple& tuple) { f(std::get<Is>(tuple)); }... 
                   ^~~ 
Compiler exited with result code 1 

Câu hỏi: ai đúng? Nó có thể được sửa?

Chương trình:

#include <iostream> 
#include <array> 
#include <tuple> 

namespace notstd { 

    template<class F, class Tuple, std::size_t...Is> 
    auto make_callers_impl(std::index_sequence<Is...>) -> decltype(auto) 
    { 
     static std::array<void (*) (F&, Tuple&), sizeof...(Is)> x = 
     { 
      +[](F& f, Tuple& tuple) { f(std::get<Is>(tuple)); }... 
     }; 
     return x; 
    }; 

    template<class F, class Tuple> 
    auto make_callers() -> decltype(auto) 
    { 
     return make_callers_impl<F, Tuple>(std::make_index_sequence<std::tuple_size<std::decay_t<Tuple>>::value>()); 
    }; 

    template<class Tuple, std::size_t N = std::tuple_size<std::decay_t<Tuple>>::value > 
    struct tuple_iterator { 
     static constexpr auto size = N; 

     constexpr tuple_iterator(Tuple& tuple, std::size_t i = 0) : tuple(tuple), i(i) {} 

     template<class F> 
     void with(F&& f) const { 
      static const auto& callers = make_callers<F, Tuple>(); 
      callers[i](f, tuple); 
     } 

     constexpr bool operator!=(tuple_iterator const& r) const { 
      return i != r.i; 
     } 

     constexpr auto operator++() -> tuple_iterator& { 
      ++i; 
      return *this; 
     } 


     Tuple& tuple; 
     std::size_t i; 
    }; 

    template<class Tuple> 
    auto begin(Tuple&& tuple) 
    { 
     return tuple_iterator<Tuple>(std::forward<Tuple>(tuple)); 
    } 

    template<class Tuple> 
    auto end(Tuple&& tuple) 
    { 
     using tuple_type = std::decay_t<Tuple>; 
     static constexpr auto size = std::tuple_size<tuple_type>::value; 
     return tuple_iterator<Tuple>(std::forward<Tuple>(tuple), size); 
    } 

} 

template<class T> void emit(const T&); 

int main() { 
    auto a = std::make_tuple(1.1, "foo", 2); 
    auto i = notstd::begin(a); 
    while(i != notstd::end(a)) 
    { 
     i.with([](auto&& val) { std::cout << val << std::endl; }); 
     ++i; 
    } 
} 
+0

Tôi đề nghị bạn thêm thẻ 'language-lawyer' vào câu hỏi này, vì nó phù hợp về trình biên dịch với tiêu chuẩn –

+0

@GuillaumeRacicot được thực hiện. cảm ơn bạn. –

+0

Vâng tôi đoán rằng Clang là đúng vì mã biên dịch và hoạt động như mong đợi và vì gcc đang ném lỗi trình biên dịch, nó không phải đã được triển khai hoặc sửa trong phiên bản hiện tại. – chbchb55

Trả lời

14

Đây là gcc bug 47226. gcc chỉ đơn giản là không cho phép sản xuất một gói mở rộng của lambdas như thế. Lỗi này vẫn tồn tại trong 7.0.


Trong trường hợp này, bạn không thực sự cần lambda và chỉ có thể tạo một hàm mẫu:

template <size_t I, class F, class Tuple> 
void lambda(F& f, Tuple& tuple) { 
    f(std::get<I>(tuple)); 
} 

static std::array<void (*) (F&, Tuple&), sizeof...(Is)> x = 
{ 
    lambda<Is,F,Tuple>... 
}; 
4

kêu vang là đúng.

Gói thông số phải được mở rộng, nhưng gcc dường như nghĩ rằng các gói tham số chưa được mở rộng ở cuối câu lệnh là lỗi. Điều này là dễ hiểu, nhưng lambdas cho phép báo cáo chỉ là một phần nhỏ của các phát biểu khác. Không có yêu cầu rằng các gói thông số được mở rộng trước khi kết thúc mỗi tuyên bố họ đang có trong

Dưới đây là một workaround inline:.

template<std::size_t I> 
using index_t=std::integral_constant<std::size_t, I> 
template<std::size_t I> 
constexpr index_t<I> index{}; 

sau đó bên trong hàm:

auto lamb = [](auto I){ 
    using I_t=decltype(I); 
    return [](F& f, Tuple& tuple) { f(std::get<I_t::value>(tuple)); }; 
    }; 
    static std::array<void (*) (F&, Tuple&), sizeof...(Is)> x = 
    { 
    +(lamb(index_k<Is>))... 
    }; 

trong đó di chuyển cơ thể lambda bên ngoài của .... Chúng tôi vượt qua hằng số theo giá trị. Bạn thậm chí có thể vượt qua các loại theo cách đó.

mẫu khác là:

template<std::size_t...Is> 
auto index_over(std::index_sequence<Is...>){ 
    return [](auto&&f)->decltype(auto){ 
    return decltype(f)(f)(index_k<Is>...); 
    }; 
} 
template<std::size_t N> 
auto index_upto(index_t<N>={}){ 
    return index_over(std::make_index_sequence<N>{}); 
} 
template<class F> 
auto array_maker(F f){ 
    return [f=std::move(f)](auto...Is)->std::array<decltype(f(index_k<0>),sizeof...(Is)>{ 
    return {{f(Is...}}; 
    }; 
} 

này cho phép bạn né tránh vấn đề của bạn hoàn toàn, và giết chết impl:

template<class F, class Tuple> 
auto make_callers() -> decltype(auto) 
{ 
    auto size=index_k<std::tuple_size<std::decay_t<Tuple>>{}>; 
    auto indexer=index_upto(size); 
    auto make_array=array_maker([](auto I){ 
    return +[](F& f, Tuple& tuple) { f(std::get<decltype(I)::value>(tuple)); }; 
    }); 
    return indexer(make_array); 
} 

mà phải thừa nhận là khá hơn lambdad.

Các vấn đề liên quan