为什么爆炸std::tuple需要std::integral_constant?
why std::integral_constant is necessary in exploding a std::tuple?
#include <iostream>
#include <tuple>
#include <iostream>
#include <utility>
template <std::size_t... Idx>
auto make_index_dispatcher(std::index_sequence<Idx...>)
{
return [](auto&& f) { (f(std::integral_constant<std::size_t, Idx>{}), ...); };
}
template <std::size_t N>
auto make_index_dispatcher()
{
return make_index_dispatcher(std::make_index_sequence<N>{});
}
template <typename Tuple, typename Func>
void for_each(Tuple&& t, Func&& f)
{
constexpr auto n = std::tuple_size<std::decay_t<Tuple>>::value;
auto dispatcher = make_index_dispatcher<n>();
dispatcher([&f, &t](auto idx) { f(std::get<idx>(std::forward<Tuple>(t))); });
}
int main()
{
for_each(std::make_tuple(1, 42.1, "hi"), [](auto&& e) {std::cout << e << ","; });
}
问题 1> 为什么我必须在下面的语句中使用 std::integral_constant<std::size_t, Idx>{}
而不是简单地 Idx
?根据我的理解,std::integral_constant<std::size_t, Idx>
是一种类型。 std::integral_constant<std::size_t, Idx>{}
是Idx
的值是真的吗?
// 确定
template <std::size_t... Idx>
auto make_index_dispatcher(std::index_sequence<Idx...>)
{
return [](auto&& f) { (f(std::integral_constant<std::size_t, Idx>{}), ...); };
}
// 错误
template <std::size_t... Idx>
auto make_index_dispatcher(std::index_sequence<Idx...>)
{
return [](auto&& f) { (f(Idx), ...); };
}
std::get
需要编译时常量表达式,而 Idx
不是编译时常量表达式,这对吗?
问题2>为什么我们不能通过引用传递std::index_sequence
?
// 错误:
auto make_index_dispatcher(std::index_sequence<Idx...>&)
谢谢
Why I have to use std::integral_constant{} instead of simply Idx in the following statement?
因为函数参数从来都不是常量表达式。您可以简单地将索引作为非类型模板参数传递,它们是 常量表达式 。这对于 C++20 模板 lambda 尤其有效:
template <std::size_t... Idx>
auto make_index_dispatcher(std::index_sequence<Idx...>)
{
return [](auto&& f) { (f.template operator()<Idx>(), ...); };
}
template <typename Tuple, typename Func>
void for_each(Tuple&& t, Func&& f)
{
constexpr auto n = std::tuple_size<std::decay_t<Tuple>>::value;
auto dispatcher = make_index_dispatcher<n>();
dispatcher([&f, &t]<auto Idx>(){ f(std::get<Idx>(std::forward<Tuple>(t))); });
}
Why we cannot pass std::index_sequence by reference?
可以,但是您需要像调用任何其他非const
引用一样使用左值调用您的函数。这编译:
template <std::size_t... Idx>
auto make_index_dispatcher(std::index_sequence<Idx...>&)
{
}
int main()
{
std::index_sequence<> is;
make_index_dispatcher(is);
}
还有,完全没用
此外,您的整个代码可以简单地是:
int main()
{
std::apply([](auto&&... xs)
{
((std::cout << xs << ','), ...);
}, std::make_tuple(1, 42.1, "hi"));
}
Why I have to use std::integral_constant{} instead of simply Idx in the following statement?
Is it true that std::get expected compile-time constant expression while Idx is NOT a compile-time constant expression?
std::get<>
需要编译时表达式,但您实际上并没有直接使用 idx
(这不是编译时表达式)。
std::integral_constant<std::size_t, I>
有一个方便的 constexpr
到 std::size_t
的转换运算符 returns I
,所以当你这样做时:
std::get<idx>(...)
...你实际上在做 std::get<(std::size_t)idx>
,而 (std::size_t)idx
是一个编译时表达式,因为转换运算符是 constexpr
.
如果不将 Idx
包裹在 std::integral_constant
中,则通用 lambda 中 idx
的类型将为 std::size_t
,以上所有操作均无效.
Why I have to use std::integral_constant{} instead of simply Idx in the following statement?
纯属娱乐...
你说的是正确的(据我所知)直到 C++17(因为 std::get()
模板参数必须在编译时已知并且使用 std::integral_constant
是一种优雅的方式在通用(C++14 或更高版本)lambda 中将编译时已知常量作为 auto
参数的类型传递;请参阅其他答案以获得更清晰的解释)但不再正确(不一定要使用 std::integral_constant
) 从 C++20 开始。
事实上,C++20 引入了模板 lambda,因此您可以按如下方式重写代码,将 Idx
值作为模板参数传递,而不传递 std::integral_constant
#include <tuple>
#include <iostream>
#include <utility>
template <std::size_t... Idx>
auto make_index_dispatcher(std::index_sequence<Idx...>)
{
return [](auto&& f) { (f.template operator()<Idx>(), ...); };
} // ......................^^^^^^^^^^^^^^^^^^^^^^^^^^^^ modified lambda call
template <std::size_t N>
auto make_index_dispatcher()
{
return make_index_dispatcher(std::make_index_sequence<N>{});
}
template <typename Tuple, typename Func>
void for_each(Tuple&& t, Func&& f)
{
constexpr auto n = std::tuple_size<std::decay_t<Tuple>>::value;
auto dispatcher = make_index_dispatcher<n>();
dispatcher([&f, &t]<std::size_t I>() { f(std::get<I>(std::forward<Tuple>(t))); });
} // ..................^^^^^^^^^^^^^^^^^ template parameter added, argument removed
int main()
{
for_each(std::make_tuple(1, 42.1, "hi"), [](auto&& e) {std::cout << e << ","; });
}
#include <iostream>
#include <tuple>
#include <iostream>
#include <utility>
template <std::size_t... Idx>
auto make_index_dispatcher(std::index_sequence<Idx...>)
{
return [](auto&& f) { (f(std::integral_constant<std::size_t, Idx>{}), ...); };
}
template <std::size_t N>
auto make_index_dispatcher()
{
return make_index_dispatcher(std::make_index_sequence<N>{});
}
template <typename Tuple, typename Func>
void for_each(Tuple&& t, Func&& f)
{
constexpr auto n = std::tuple_size<std::decay_t<Tuple>>::value;
auto dispatcher = make_index_dispatcher<n>();
dispatcher([&f, &t](auto idx) { f(std::get<idx>(std::forward<Tuple>(t))); });
}
int main()
{
for_each(std::make_tuple(1, 42.1, "hi"), [](auto&& e) {std::cout << e << ","; });
}
问题 1> 为什么我必须在下面的语句中使用 std::integral_constant<std::size_t, Idx>{}
而不是简单地 Idx
?根据我的理解,std::integral_constant<std::size_t, Idx>
是一种类型。 std::integral_constant<std::size_t, Idx>{}
是Idx
的值是真的吗?
// 确定
template <std::size_t... Idx>
auto make_index_dispatcher(std::index_sequence<Idx...>)
{
return [](auto&& f) { (f(std::integral_constant<std::size_t, Idx>{}), ...); };
}
// 错误
template <std::size_t... Idx>
auto make_index_dispatcher(std::index_sequence<Idx...>)
{
return [](auto&& f) { (f(Idx), ...); };
}
std::get
需要编译时常量表达式,而 Idx
不是编译时常量表达式,这对吗?
问题2>为什么我们不能通过引用传递std::index_sequence
?
// 错误:
auto make_index_dispatcher(std::index_sequence<Idx...>&)
谢谢
Why I have to use std::integral_constant{} instead of simply Idx in the following statement?
因为函数参数从来都不是常量表达式。您可以简单地将索引作为非类型模板参数传递,它们是 常量表达式 。这对于 C++20 模板 lambda 尤其有效:
template <std::size_t... Idx>
auto make_index_dispatcher(std::index_sequence<Idx...>)
{
return [](auto&& f) { (f.template operator()<Idx>(), ...); };
}
template <typename Tuple, typename Func>
void for_each(Tuple&& t, Func&& f)
{
constexpr auto n = std::tuple_size<std::decay_t<Tuple>>::value;
auto dispatcher = make_index_dispatcher<n>();
dispatcher([&f, &t]<auto Idx>(){ f(std::get<Idx>(std::forward<Tuple>(t))); });
}
Why we cannot pass std::index_sequence by reference?
可以,但是您需要像调用任何其他非const
引用一样使用左值调用您的函数。这编译:
template <std::size_t... Idx>
auto make_index_dispatcher(std::index_sequence<Idx...>&)
{
}
int main()
{
std::index_sequence<> is;
make_index_dispatcher(is);
}
还有,完全没用
此外,您的整个代码可以简单地是:
int main()
{
std::apply([](auto&&... xs)
{
((std::cout << xs << ','), ...);
}, std::make_tuple(1, 42.1, "hi"));
}
Why I have to use std::integral_constant{} instead of simply Idx in the following statement?
Is it true that std::get expected compile-time constant expression while Idx is NOT a compile-time constant expression?
std::get<>
需要编译时表达式,但您实际上并没有直接使用 idx
(这不是编译时表达式)。
std::integral_constant<std::size_t, I>
有一个方便的 constexpr
到 std::size_t
的转换运算符 returns I
,所以当你这样做时:
std::get<idx>(...)
...你实际上在做 std::get<(std::size_t)idx>
,而 (std::size_t)idx
是一个编译时表达式,因为转换运算符是 constexpr
.
如果不将 Idx
包裹在 std::integral_constant
中,则通用 lambda 中 idx
的类型将为 std::size_t
,以上所有操作均无效.
Why I have to use std::integral_constant{} instead of simply Idx in the following statement?
纯属娱乐...
你说的是正确的(据我所知)直到 C++17(因为 std::get()
模板参数必须在编译时已知并且使用 std::integral_constant
是一种优雅的方式在通用(C++14 或更高版本)lambda 中将编译时已知常量作为 auto
参数的类型传递;请参阅其他答案以获得更清晰的解释)但不再正确(不一定要使用 std::integral_constant
) 从 C++20 开始。
事实上,C++20 引入了模板 lambda,因此您可以按如下方式重写代码,将 Idx
值作为模板参数传递,而不传递 std::integral_constant
#include <tuple>
#include <iostream>
#include <utility>
template <std::size_t... Idx>
auto make_index_dispatcher(std::index_sequence<Idx...>)
{
return [](auto&& f) { (f.template operator()<Idx>(), ...); };
} // ......................^^^^^^^^^^^^^^^^^^^^^^^^^^^^ modified lambda call
template <std::size_t N>
auto make_index_dispatcher()
{
return make_index_dispatcher(std::make_index_sequence<N>{});
}
template <typename Tuple, typename Func>
void for_each(Tuple&& t, Func&& f)
{
constexpr auto n = std::tuple_size<std::decay_t<Tuple>>::value;
auto dispatcher = make_index_dispatcher<n>();
dispatcher([&f, &t]<std::size_t I>() { f(std::get<I>(std::forward<Tuple>(t))); });
} // ..................^^^^^^^^^^^^^^^^^ template parameter added, argument removed
int main()
{
for_each(std::make_tuple(1, 42.1, "hi"), [](auto&& e) {std::cout << e << ","; });
}