このページはC++26に採用される見込みの言語機能の変更を解説しています。
のちのC++規格でさらに変更される場合があるため関連項目を参照してください。
概要
C++26では、構造化束縛でタプルを分解する際に、パックで受け取れるようになる。この記法はテンプレート内でのみ利用できる。
std::tuple<X, Y, Z> f();
template <class>
void g()
{
auto [x, y, z] = f(); // C++23: OK, C++26: OK
auto [...xs] = f(); // C++26: OK. パックxsは長さ3でX, Y, Zが含まれる
auto [x, ...rest] = f(); // C++26: OK. xはX、パックrestは長さ2でYとZが含まれる
auto [x, y, z, ...rest] = f(); // C++26: OK. restは空のパック
auto [x, ...rest, z] = f(); // C++26: OK. xはX、パックrestは長さ1でYに対応、zはZ
auto [...a, ...b] = f(); // NG: 複数のパックは指定できない
}
void h()
{
auto [x, y, z] = f(); // C++23: OK, C++26: OK
auto [...xs] = f(); // NG: テンプレートの外では不適格となる
}
std::apply()の実装改善
タプルを展開して指定した関数の引数として転送して実行するstd::apply()関数のC++23での実装:
namespace detail {
template <class F, class Tuple, std::size_t... I>
constexpr decltype(auto) apply_impl(F &&f, Tuple &&t, std::index_sequence<I...>)
{
return std::invoke(std::forward<F>(f),
std::get<I>(std::forward<Tuple>(t))...);
}
}
template <class F, class Tuple>
constexpr decltype(auto) apply(F &&f, Tuple &&t)
{
return detail::apply_impl(
std::forward<F>(f), std::forward<Tuple>(t),
std::make_index_sequence<std::tuple_size_v<
std::decay_t<Tuple>>>{});
}
C++26での実装:
namespace detail {
template <class A, class B>
using override_ref = std::conditional_t<std::is_rvalue_reference_v<A&&>, B&&, B&>;
}
template <class F, class Tuple>
constexpr decltype(auto) apply(F &&f, Tuple &&t)
{
auto&& [...elems] = t;
return std::invoke(std::forward<F>(f),
static_cast<detail::override_ref<Tuple, decltype(elems)>>(elems)...);
}
複数のタプルに対して畳み込み式を実行する
C++23の場合:
template <class P, class Q>
auto dot_product(P p, Q q) {
return std::apply([&](auto... p_elems){
return std::apply([&](auto... q_elems){
return (... + (p_elems * q_elems));
}, q)
}, p);
}
C++26の場合:
template <class P, class Q>
auto dot_product(P p, Q q) {
// applyが必要なくなる
auto&& [...p_elems] = p;
auto&& [...q_elems] = q;
return (... + (p_elems * q_elems));
}