接受具有特定返回类型的函数类型的c++模板函数

C++ template function that takes a function type with specific return type

本文关键字:函数 类型 c++ 返回类型      更新时间:2023-10-16

我正在用c++的模板做一些魔术,想尝试一下。

在这种情况下,我写了一个朴素的泛型列表实现,List类型和ListElement类型包含数据。

现在我已经编写了一个模板化的"for each call"函数,它接受任意类型的成员函数类型,该类型存储在具有任意参数列表的列表中,并使用给定的参数对列表中的每个元素调用该成员函数:

template<typename function, typename ... arguments>
void for_each_call(function call, arguments ... args)
{
    for(ListElement * current = this->first; current != nullptr; current = current->next)
    {
        (current->value->*call)(args ...);
    }
}

这样做的问题是,我不能对被调用函数的返回值"反应"。虽然我不想实现.map功能!

现在我想实现一个"for each call until",它对列表中的值调用函数,直到调用返回"true",然后停止。为此,我需要将作为模板参数插入的函数限制为特定返回布尔值的任何类型的函数。我四处输入,直到编译器停止抱怨,得到这个:

template<bool (*function), typename ... arguments>
void for_each_call_until(arguments ... args)
{
    for(ListElement * current = this->first; current != nullptr; current = current->next)
    {
        if((current->value->*function)(args ...)) break;
    }
}

这里发生了什么,这是正确的方法吗?如果不是,什么是正确的方法?

编辑:由于有些人建议使用std::命名空间中的函数:在这些小的培训课程中,我尽量避免使用std::,就好像我想要使用std::一样,我不会自己编写这些像列表、向量或映射这样的小的标准化实现,而是使用std::或boost::

首先,这种方法是不必要的限制:

(current->value->*call)(args ...);

如果要求成员函数,则实际上只能执行少量操作。如果打电话的人想做更多,他们就完蛋了。相反,泛化并传递current->value作为第一个参数:

template<typename function, typename ... arguments>
void for_each_call(function call, arguments ... args)
{
    for(ListElement * current = this->first; current != nullptr; current = current->next)
    {
        call(current->value, args...);
    }
}

这适用于所有的情况,就像以前一样-以前你会传递&Class::mem,而不是传递std::mem_fn(&Class::mem) -但现在你可以传递任何类型的可调用对象。


现在进入你的主要问题。你不需要做任何不同的事情。只使用call(): 的结果
template<typename function, typename ... arguments>
void for_each_call(function call, arguments ... args)
{
    for(ListElement* current = this->first; current != nullptr; current = current->next)
    {
        if (call(current->value, args...)) {
            break;
        }
    }
}

就是这样。如果用户提供的可调用对象没有返回上下文可转换为bool的内容,则会得到编译错误。为什么限制只返回bool ?

如果你真的只需要bool,抛出一个静态断言:

template<typename function, typename ... arguments>
void for_each_call(function call, arguments ... args)
{
    static_assert(std::is_same<decltype(call(this->first->value, args...)), bool>::value, "Must be bool!");
    // rest...
}

注意:您可能希望通过引用const来获取arguments...,以避免大量复制。

从这里开始,向您展示成员函数指针是如何工作的:

class Foo {
 public:
  bool test() { return true; }
};
/// The function takes a member function of a class T and its arguments.
template<typename T, typename... Args>
void for_each_call_until(bool (T::*member_function)(Args...),
                         Args&& ... args) {
  T obj;  // Instantiate an example object.
  bool rts = (obj.*member_function)(std::forward<Args>(args)...);
  if (rts == false) {  // Check for the result of the member function
    // close
  }
  // ...
}

你的函数可以像这样:

template<typename... Args>
void for_each_call_until(bool (ListElement::*member_function)(Args...),
                         Args&& ... args) {
  for ( /* iteration over pointers */ ) {
    bool rts = (current->*member_function)(std::forward<Args>(args)...);
    if (rts == false) {
      // break
    }
    // ...
}
}

一个简单的解决方案是使用部分专门化来强制编译错误:

#include <type_traits>
#include <utility>
template<typename T> struct is_bool;
template<> struct is_bool<bool> { typedef int value; };
template<typename function, typename ... arguments>
void for_each_call(function call, arguments && ... args)
{
    typedef decltype(call(std::forward<arguments>(args)...)) ret_type;
    typedef typename is_bool<ret_type>::value x;
    call(std::forward<arguments>(args)...);
}
bool foo(int, int) {}   // Compiles
// int foo(int, int) {}   // Does not compile
int main()
{
    for_each_call(foo, 4, 2);
    return 0;
}