std::function::argument_type 的替代方案是什么?

17
根据cppreference.com,以下三个成员类型:argument_typefirst_argument_typesecond_argument_type在C++17中被弃用,在C++20中被移除。
那么这些成员类型的标准库替代品是什么呢?我是指我可以编写自己的类型特征,但我怀疑没有合适的替代品会被移除。作为一个例子:
template <typename F> 
void call_with_user_input(F f) {
    typename F::first_argument_type x;  // what to use instead ??
    std::cin >> x;
    f(x);
}

你可以为此创建一个特征。作为奖励,你的方法也将与lambda一起工作。 - Jarod42
获取模板可调用对象的参数类型。 - NathanOliver
@NathanOliver 的最佳答案中提到:“[...] 你无法获取其参数类型”,这并不是 argument_type 的替代品 ;) - 463035818_is_not_a_number
@user463035818,您希望F成为任何可调用类型还是仍然是std::function?我问这个问题是因为first_argument_type只存在于std::function中。Lambda表达式、函数和大多数函数对象都没有它。而且,正如第一个答案所示,我认为这在通用情况下是不可能的,因为operator()可以被重载。 - NathanOliver
@NathanOliver 只有 std::function,如果不清楚,很抱歉,我不想让示例过于复杂。 - 463035818_is_not_a_number
1
所以 template <typename Ret, typename FirstArg> void call_with_user_input(std::function<Ret(FirstArg)> f) :-) 作为奖励,它可以处理不止前两个参数。 - Jarod42
4个回答

3
你可以通过引入模板参数来获取类型。
template <typename Ret, typename Arg> 
void call_with_user_input(std::function<Ret(Arg)> f) {
    Arg x;
    std::cin >> x;
    f(x);
}

将参数类型作为模板参数提供给您。作为奖励,如果您需要,还可以获得返回类型。


其实现在我在想为什么一开始会有一个first_argument。看起来删除它并没有实质性的后果。 - 463035818_is_not_a_number
2
@user463035818 这是因为许多旧的C++03机制需要它,但这些机制已被删除。请参见此链接以了解更多信息:https://dev59.com/8VsV5IYBdhLWcg3wsQnf - NathanOliver
事实上,这些typedef比无用还糟糕...这最终使我信服。我曾被误导以为它们对某些东西是必需的。 - 463035818_is_not_a_number
@user463035818 我认为有一个arguments_type类型作为Arg元组会很有用... 引入更多的模板参数有时可能会很麻烦,因为已经有了result_type,所以这是有道理的。 - jdehesa

2
据我所知,它们将被删除,就是这样。 我在这里找到了提案first_argument_typesecond_argument_type相关:

可适应的函数绑定是C++17中被删除的强有力的候选项,但之所以保留,只是因为没有足够的替代方案供一元/二元否定者的用户迁移。该功能std::not_fn已添加到C++17中,以允许迁移路径,

查看c++17中的std::not_fn时,我发现:

请注意,由于添加了新的语言功能和库(例如lambda表达式,“菱形”函数等),可适应的函数协议不再像最初设计的那样运行良好。这不是由于缺乏努力,而只是因为对于某些类型,例如多态lambda对象,不可能拥有唯一的typedef集。但是,由于在库中支持其他地方而付出了代价,因此我们需要在几个组件中使用尴尬的有条件定义的成员typedef,例如std::function将一个具有恰好一个或两个参数的函数类型包装起来,或类似地用于仅具有一或两个参数的函数引用的std::reference_wrapper。

这意味着它们即将被删除。 first_argument_typesecond_argument_type的问题之一似乎是由于多态lambda对象
另外,正如评论中指出的那样,任何可以传递给std::variant<...>::visit的具有多个operator()的内容都存在first_argument_type的问题。

1
任何具有多个 operator() 的内容,不仅限于多态 lambda。这包括您可以有用地传递给 std::variant<...>::visit任何 内容。 - Caleth

1

一种方法是使用boost::function_types

#include <boost/function_types/parameter_types.hpp>
#include <boost/mpl/at.hpp>

template <typename F> 
void call_with_user_input(F f) {
    using FnType = decltype(&F::operator());
    using FirstArgType = typename boost::mpl::at_c<boost::function_types::parameter_types<FnType>, 0>::type;
    FirstArgType x;
    std::cin >> x;
    f(x);
}

0

不幸的是,我相信这必须使用标准的 SFINAE 类型特性实现技术来完成。以下是一个替代示例,用于提取所有模板参数或任何特定模板参数应该相对简单。

我怀疑在大多数情况下,您希望将模板类型衰变,但我不知道标准的实现是否已经进行了衰变。

#include <functional>
#include <tuple>

template<typename... T>
struct extract_first_arg_type;

template<typename RetType, typename... ValTypes> 
struct extract_first_arg_type<std::function<RetType(ValTypes...)>> {
    using type = std::decay_t<std::tuple_element_t<0, std::tuple<ValTypes...>>>;
};


std::function<void(int, double)> f = [](int k, double d) {};
static_assert(std::is_same_v<extract_first_arg_type<decltype(f)>::type, int>, "");
static_assert(!std::is_same_v<extract_first_arg_type<decltype(f)>::type, double>, "");

godbolt link

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接