【问题标题】:C++ template function that takes a function type with specific return type采用具有特定返回类型的函数类型的 C++ 模板函数
【发布时间】:2016-08-10 22:01:05
【问题描述】:

我正在用 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) 的返回值分配给一个布尔局部变量。除非函数实际返回布尔值,否则此分配将无法编译,因此实际上您将函数限制为仅接受返回布尔值的函数。如果我没记错的话,产生的编译错误实际上是非常有用的,例如“无法将 转换为布尔值。”
  • 如果函数返回一个int,这种方法也可以,这是错误的。
  • 好吧,我们谈论的是 c++,作为返回类型的 bool 和 int 背后的语义以我们都不喜欢思考的方式交织在一起。
  • @SamVarshavchik bool 实际上在我认为的某些标准版本中类型定义为 int 。如果您真的需要这种安全性,那么定义您自己的类型安全 bool 枚举然后只接受输出该枚举成员的函数怎么样。
  • 我不需要那种安全性,这真的只是我想到的将某些东西传递给“如果”的最好方法,不需要考虑太多

标签: c++ templates


【解决方案1】:

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

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

如果您需要一个成员函数,那么您实际上可以执行的操作很少。如果来电者想要做更多,他们就有点搞砸了。相反,概括并传递current-&gt;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...);
    }
}

这适用于以前的所有情况 - 之前你会传递 &amp;Class::mem 而不是现在传递 std::mem_fn(&amp;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... 以避免大量副本。

【讨论】:

  • 第一种方法我也想过,但我得到编译错误:错误:>>.*>->*>call (...)>(... -> *call)(...)
  • @salbeira 我不知道你在说什么。你用mem_fn()了吗?
  • 何时何地?并且可能不是因为我在培训课程中避免使用 std:: 是因为如果我使用 std:: 我就不需要编写这些个人成功的小 sn-ps :-P
  • @salbeira 我在回答中说的。另请阅读参考资料。避免使用标准库也很愚蠢。
  • 我这样做是为了了解 std:: 做什么以及为什么做这些事情。 mem_fn 包装器的构造似乎是我自己尝试在没有 500 个边缘案例的情况下自己实现的东西,我只是尝试阅读 libc++ 函数.h 并想“哇,我什么都不明白,是时候自己尝试一下了” - - 也是的 - 我这样做是因为它既愚蠢又有趣
【解决方案2】:

从这里开始,只是为了向你展示成员函数指针是如何工作的

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
    }
    // ...
}
}

【讨论】:

    【解决方案3】:

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

    #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;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-03-04
      • 1970-01-01
      • 1970-01-01
      • 2012-06-21
      • 2017-04-04
      • 2019-04-18
      相关资源
      最近更新 更多