2017-04-12 14 views
11

當試用方便的方式來訪問元組作爲容器時,我寫了一個測試程序。哪個編譯器(如果有的話)在參數包擴展中有缺陷?

上鐺(3.9.1,和蘋果鐺)

它編譯如預期,產生預期的輸出:

1.1 
foo 
2 

上的gcc(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

我建議你在這個問題上添加標籤'language-lawyer',因爲它是關於編譯器符合標準 –

+0

@GuillaumeRacicot這樣做的。謝謝。 –

+0

嗯,我猜想,鏗鏘是對的,因爲代碼編譯和按預期工作,因爲海灣合作委員會正在拋出編譯器錯誤,它不能在當前版本中執行或修復。 – chbchb55

回答

14

這是gcc bug 47226。海灣合作委員會根本不允許生產一包像這樣的lambda的擴展。該錯誤仍然存​​在於7.0中。


在這種情況下,你並不真正需要的Lambda和可以只創建一個函數模板:

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

鐺是正確的。

參數包必須展開,但gcc似乎認爲聲明結尾的未擴展參數包是錯誤的。這是可以理解的,但lambda允許聲明只是其他聲明的一小部分。不要求那個參數包每次他們在發言結束前擴大

這裏是一個內聯解決方法:

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。

相關問題