哪个编译器(如果有的话)在参数包扩展中存在错误
Which compiler, if any has a bug in parameter pack expansion?
在尝试使用方便的方法访问元组作为容器时,我编写了一个测试程序。
在 Clang(3.9.1 和 Apple Clang(上,它按预期编译,产生预期的输出:
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;
}
}
这是 gcc 错误 47226。 GCC 根本不允许产生这样的 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>...
};
clang 是对的。
参数包必须展开,但 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>))...
};
将 lambda 体移出...
. 我们按值传递常量。 您甚至可以以这种方式传递类型。
另一种模式是:
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。
相关文章:
- 嵌套参数包扩展失败
- [temp.variadic]中关于包扩展实例化的措辞
- 模板参数部分中有关包扩展的一些混淆
- 如何使用 std::forward 精确地评估参数包的扩展?
- 参数包没有扩展'...'即使...被使用
- 如何"unzipping"扩展参数包模式?
- 可变参数函数参数包扩展
- c++非类型参数包扩展
- C++别名的模板参数包扩展
- 如何将参数包扩展为向量<any>
- 模板包扩展以将函数应用于连续的参数对
- 模板 使用数据数组调用函数时扩展参数包
- C++参数包扩展
- 如何为原生UI工具包扩展Ranorex?
- 折叠表达式、参数包扩展、类成员函数中的递归
- 将代码移出类定义时未扩展参数包
- xgboost 构建错误:参数包未扩展为"..":
- 可变参数模板:错误:参数包未扩展'...'
- 使用模板包进行扩展
- 使用可变包类型扩展的C++函数调用程序包装