2017-04-12 12 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。

相关问题