根据包的结果创建 std::tuple

问题描述 投票:0回答:1

假设我正在编写一个名为“invoke_all”的函数,它将它接收到的所有 lambda(作为模板参数)应用于参数,它看起来像:

template <auto... fs, typename... Args>
constexpr auto invoke_all(Args &&...args) {
  (std::invoke(fs, std::forward<Args>(args)...), ...);
}

到目前为止一切顺利。称呼它为:

invoke_all<
      [](auto i, auto j) { std::println("lambda1: {} {}", i, j); },
      [](auto i, auto j) { std::println("lambda2: {} {}", i, j); }
      >
      (42, "abc"s);

但是假设我实际上希望这些 lambda 返回值,并将所有返回值打包在 std::tuple 中,问题就出现了。这无法编译:

#include <functional>
#include <tuple>
#include <format>
#include <string>
#include <cassert>

template <auto... fs, typename... Args>
constexpr auto invoke_all(Args &&...args) {
  return std::tuple{(std::invoke(fs, std::forward<Args>(args)...), ...)};
}

using namespace std::literals;

int main() {
    auto results = invoke_all< //
        [](auto i, auto j) { return std::format("lambda1: {} {}", i, j); },
        [](auto i, auto j) { return std::format("lambda2: {} {}", i, j); }> //
    (42, "abc"s);

    assert((results == std::tuple{"lambda1: 42 abc"s, "lambda2: 42 abc"s}));
}

因为“invoke_all”返回的元组是大小为 1 的元组,而不是 2。

这也不能编译:

#include <functional>
#include <tuple>
#include <format>
#include <string>
#include <cassert>

template <auto... fs, typename... Args>
constexpr auto invoke_all(Args &&...args) {
  return std::tuple{std::invoke(fs, std::forward<Args>(args)...), ...};
}

using namespace std::literals;

int main() {
    auto results = invoke_all< //
        [](auto i, auto j) { return std::format("lambda1: {} {}", i, j); },
        [](auto i, auto j) { return std::format("lambda2: {} {}", i, j); }> //
    (42, "abc"s);

    assert((results == std::tuple{"lambda1: 42 abc"s, "lambda2: 42 abc"s}));
}

因为包扩展周围缺少 () (我认为)。

那么有没有办法解决这个问题,即从结果包扩展中创建一个元组?

c++ tuples
1个回答
2
投票

你上一次的尝试几乎就成功了。您需要记住的是

...
表示扩展为逗号分隔列表,因此当您这样做时

template <auto... fs, typename... Args>
constexpr auto invoke_all(Args &&...args) {
  return std::tuple{std::invoke(fs, std::forward<Args>(args)...), ...};
}

您正在尝试执行逗号表达式,但缺少执行折叠表达式所需的外部 ()。相反,你想要的是

template <auto... fs, typename... Args>
constexpr auto invoke_all(Args &&...args) {
  return std::tuple{std::invoke(fs, std::forward<Args>(args)...)...};
}

没有

,
。这扩展到

template <auto... fs, typename... Args>
constexpr auto invoke_all(Args &&...args) {
  return std::tuple{std::invoke(fs1, fwd_arg1, fwd_arg2, ..., fwd_argN), 
                    std::invoke(fs2, fwd_arg1, fwd_arg2, ..., fwd_argN)
                    ...,
                    std::invoke(fsN, fwd_arg1, fwd_arg2, ..., fwd_argN);
}

测试用例需要注意的另一件事是,结果不会是

std::tuple{"lambda1: 42 abc"s, "lambda2: 42 abc"s}

但是却是

std::tuple{"lambda1: 42 abc"s, "lambda2: 42"s}

这是因为

"abc"s
是一个临时变量,因此它会被移动到第一个 lambda 中,然后剩下的 lambda 剩下一个空字符串。理想情况下,您不应该转发任何内容,而只是按值传递,因此所有内容都被视为左值,例如

template <auto... fs, typename... Args>
constexpr auto invoke_all(Args &&...args) {
  return std::tuple{std::invoke(fs, args...)...};
}
© www.soinside.com 2019 - 2024. All rights reserved.