什么时候c++17 std::apply/invoke是编程所必需的?

yfwxisqw  于 2023-02-06  发布在  其他
关注(0)|答案(2)|浏览(235)

只要c++是面向对象的编程语言,我不确定std::applystd::invoke是否是必要的实用程序,它转换:

Object.Function(Args...)

转换为:

std::invoke(Object, Function, Args)

这看起来像是c风格的函数调用,那么我的问题是,这种场景什么时候成为日常编程的必要/便利?或者,是否存在使用apply/invoke使事情变得更简单的情况?
你能帮我举一个好的例子吗?谢谢!

xpcnnkqh

xpcnnkqh1#

std::invoke使所有Callable对象能够被“统一”调用,其中包含指向成员函数的指针和指向不能用像f(args...)这样的常规函数调用形式调用的成员变量的指针

struct S {
  void f(int i);
  int x;
};

int main() {
  auto mem_vptr = &S::x;
  auto mem_fptr = &S::f;
  S s;
  std::invoke(mem_vptr, s);    // invoke like s.*mem_vptr;
  std::invoke(mem_fptr, s, 0); // invoke like (s.*mem_fptr)(0);
}
bhmjp9jg

bhmjp9jg2#

std::apply的一个实际用途是元组解包,可能是嵌套的。

#include <iostream>

#include <string>
#include <tuple>
#include <sstream>

// adapted from here: https://stackoverflow.com/a/48458312
template <typename> 
constexpr bool is_tuple_v = false;

template <typename ...T> 
constexpr bool is_tuple_v<std::tuple<T...>> = true;

template<typename Tval, typename ... T>
void linearize_tuple(std::stringstream &outbuf, const Tval& arg, const T& ... rest) noexcept {
    if constexpr (is_tuple_v<Tval>){
        outbuf << "{ ";
        std::apply([&outbuf](auto const&... packed_values) {
                linearize_tuple(outbuf, packed_values ...);
            }, arg
        );
        outbuf << " }";
    }
    else{
        outbuf << arg;
    }

    if constexpr(sizeof...(rest) > 0){
        outbuf << ' ';
        linearize_tuple(outbuf, rest ...);
    }
}

template<typename ... T>
std::string args_to_string(const T& ... args) noexcept {
    std::stringstream outbuf{};
    if constexpr(sizeof...(args) > 0){
        linearize_tuple(outbuf, args ...);
    }
    return outbuf.str();
}

int main(){
    std::cout << args_to_string(
        "test", 1, "2", 3.0, '0', std::tuple
        {
            "examination", 10, "20", 30.0, '1', std::tuple
            {
                "we need to go deeper", 100, "200", 300, '2'
            }
        }
    );
}

它将打印:

test 1 2 3 0 { examination 10 20 30 1 { we need to go deeper 100 200 300 2 } }

如果你看一下std::apply的实现,它可能使用std::invoke,就像example from cppreference一样。关键部分:

namespace detail {
template <class F, class Tuple, std::size_t... I>
constexpr decltype(auto) apply_impl(F&& f, Tuple&& t, std::index_sequence<I...>)
{
    // This implementation is valid since C++20 (via P1065R2)
    // In C++17, a constexpr counterpart of std::invoke is actually needed here
    return std::invoke(std::forward<F>(f), std::get<I>(std::forward<Tuple>(t))...);
}

相关问题