2017-04-12 6 views
11

कंटेनर के रूप में टुपल्स तक पहुंचने के सुविधाजनक तरीकों के साथ प्रयोग करते समय, मैंने एक परीक्षण कार्यक्रम लिखा था।कौन सा कंपाइलर है, अगर किसी पैरामीटर पैक विस्तार में कोई बग है?

बजना (3.9.1, और सेब बजना) पर

यह संकलित की उम्मीद के रूप में, उम्मीद उत्पादन उत्पादन:

1.1 
foo 
2 
जीसीसी पर

(5.4, 6.3), यह संकलन करने में विफल रहता है:

<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 

प्रश्न: कौन सही है? क्या इसे ठीक किया जा सकता है?

कार्यक्रम:

#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

मैं आपको इस प्रश्न में टैग 'भाषा-वकील' जोड़ने का सुझाव देता हूं, क्योंकि यह मानक –

+0

@GuillaumeRacicot पर संकलक अनुरूपता के बारे में है। धन्यवाद। –

+0

वैसे मुझे लगता है कि क्लैंग सही है क्योंकि कोड संकलित और अपेक्षा के अनुसार काम करता है और चूंकि जीसीसी संकलक त्रुटियों को फेंक रहा है, इसे वर्तमान संस्करण में लागू या तय नहीं किया जाना चाहिए था। – chbchb55

उत्तर

14

यह gcc bug 47226 है। जीसीसी बस उस तरह के लैम्बडास के एक पैक विस्तार का उत्पादन करने की अनुमति नहीं देता है। बग अभी भी 7.0 में मौजूद है।


इस मामले में, तुम सच में लैम्ब्डा जरूरत नहीं है और बस एक समारोह टेम्पलेट बना सकते हैं:

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

बजना सही है।

पैरामीटर पैक का विस्तार किया जाना चाहिए, लेकिन जीसीसी को लगता है कि एक बयान के अंत में अप्रत्याशित पैरामीटर पैक त्रुटियां हैं। यह समझ में आता है, लेकिन lambdas कथन बयान अन्य बयानों का सिर्फ एक छोटा सा हिस्सा होने के लिए अनुमति देता है। कोई आवश्यकता है कि पैरामीटर पैक हर बयान वे में हैं के अंत से पहले विस्तार किया है

यहाँ एक इनलाइन समाधान नहीं है:।

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{}; 
तो समारोह के अंदर

:

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

जो ले जाता है ... के बाहर लैम्ब्डा निकाय। हम निरंतर मूल्य से गुजरते हैं। आप इस तरह से प्रकार भी पारित कर सकते हैं।

एक और पैटर्न है:

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

यह आप पूरी तरह से आपकी समस्या को चकमा देता है, और 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); 
} 

जो वैसे नहीं बल्कि lambdad खत्म हो गया है।

संबंधित मुद्दे