你如何创建一个可以采用不同参数的函数指针向量?

时间:2017-08-24 18:33:11

标签: c++ vector

我试图学习如何在std :: vector中存储函数(或者指向函数的指针)。我有这段代码:

#include <iostream>
#include <vector>

void x(int i)
{
    std::cout << "x is " << i << std::endl;
}

void y(int i, int j)
{
    std::cout << "y is " << i << " and " << "j" << std::endl;
}

int main()
{
    std::vector<void(*)(int)> V;

    V.push_back(x);

    V[0](1);

    return 0;
}

这很有效,但问题是我不能将函数y推入同一个向量,因为它需要2个整数而不是1个整数。

如何将两个函数存储在同一个向量中?

8 个答案:

答案 0 :(得分:4)

你想要的既不可能也不合理。这是不可能的,因为键入了函数指针,并且指向void(int, int)的指针与指向void(int)的指针的类型不同。 vector同类容器;它的所有元素必须是同一类型。这两种类型是无关的;你不能将指向一种类型的指针转​​换成指向另一种类型的指针,并期望调用它。

您可以做的最好的事情是使用指向不同函数类型的variant指针。现在,我不知道你将如何调用这些函数,因为不同的类型采用不同的参数列表。你怎么能通过visit或仿函数来调用它?您是否有足够的参数转发到相关功能?如果没有,那么重点是什么?

除非您事先知道列表中的索引X具有特定的参数列表,并且您将这些参数传递给它,否则没有有效的方法来调用它。如果您确实知道这一点,那么您可能需要的是tuplestruct函数指针,而不是它们的运行时容器。

答案 1 :(得分:4)

没有好的方法可以做你想做的事,但你可以做到。

编写一个扩充变量(std或boost或手动滚动标记的类型安全联合),如果你得到错误的类型,则支持带有异常的隐式强制转换(如果需要,可以随意支持类型之间的转换)。请拨打此poly_arg<Ts...>

编写一个以arg类型作为模板参数的类型橡皮擦。然后它在构造中获取一个函数指针,并且类型擦除使用正确长度的参数向量调用它。 (或函数对象和arg计数范围)。然后它有一个vararg operator(),它将它的参数转发到它的arg类型的向量,然后尝试使用上面的类型擦除调用。如果传递的参数数量错误,则会引发异常。请拨打此vararg_func<T>

存储vararg_func<poly_arg<int, double, std::string>>的矢量(3种类型的列表就是一个例子)。这可以存储void(*)(int)void(*)(int,int)以及void(*)(std::string, double, int)void(*)()等,您可以调用它。如果你的参数计数错误,你会得到一个例外(来自vararg func)。如果你得到一个参数类型错误,例外(来自poly arg)。如果传递一个不兼容的函数指针,请在push_back编译错误(这很棒!)

如果您只需要支持int args,则可以跳过poly_arg并存储vararg_func<int>

我认为这是一个糟糕的计划。

您很少想要统一处理具有不同数量和类型的参数的函数。少数合法案例最好用两个耦合型擦除系统处理(如带有非统一签名的高效大规模定制点表),在内部隐藏类型不安全。

相反,这个计划符合您的要求,这会迫使其界面中出现类型不安全的情况,并使用“dunno,也许它可以正常工作”来调用您的代码。

如果您想要帮助实现这些类型的橡皮擦,请意识到我都知道如何编写它们以及它们如何解决您的问题,在我看来,这是一个非常糟糕的主意。如果这无法阻止你,那么去学习C ++中的类型擦除和值类型多态,以及std::function如何工作。尝试写一个玩具std::function。使用“仅查看”和“仅移动”版本。尝试使用有界函数对象大小进行零分配。这应该需要几周或几年。

现在写一些更简单的案例,比如打印到ostream。足够好了。在这一点上,vararg_func应该具有挑战性但可行;试试吧。如果失败,请求SO提供帮助,包括您的尝试。

poly_arg应该比较容易。

答案 2 :(得分:1)

如果您有权访问C ++ 17,则可以使用std::variant

#include <variant>

std::vector<std::variant<void(*)(int), void(*)(int, int)>> V;

V.push_back(x);
V.push_back(y);

但是这会变得非常混乱(如果你想添加更多的函数类型等)并且因为有不同的参数类型和数量,除非你还存储它们的类型信息,否则没有确定的方法从向量中统一调用它们和std::get正确的变体。

答案 3 :(得分:0)

另一种可能性是通过添加一个可以被x体忽略的额外int参数来改变'x'的签名以匹配'y'的签名。

答案 4 :(得分:0)

易。将参数放入结构或基类中。

如果使用指向基类的指针,则可以扩展通用性。

一种老式的方法是传递一个if (message.windowid != document.windowid) 指针并让函数正确地投射它。

答案 5 :(得分:0)

实际上,只要不能将不同类型的不同对象推入向量中,就不能将不同签名函数的不同指针推送到向量中。

class A{};
class B{};

A aObj;
B bObj;
std::vector<class A> vecA;

vecA.push_back(aObj); // ok
vecA.push_back(vecB); // error
  • 仅推送与矢量实例要求的类型相同的对象:

    #include "stdafx.h"
    #include <iostream>
    #include <vector>
    
    
    void    Foo()  { std::cout << "Foo()" << std::endl; }
    void    Foo2() { std::cout << "Foo2()" << std::endl; }
    
    int     Bar(float) { std::cout << "Bar(float)" << std::endl; return 0; }
    double  Baz(int, int) { std::cout << "Baz(int, int)" << std::endl; return 0;    }
    
    
    
    int main(){
        std::system("color 1f");
    
    
        typedef void(*pFunc1)();
        typedef int(*pFunc2)(float);
        typedef double(*pFunc3)(int, int);
    
        pFunc1 pFn1 = Foo;
        pFunc1 pFn2 = Foo2;
        //pFunc1 pFn3 = Bar; // error here I guess you k now why
    
        std::vector<pFunc1> pvcFunc1;
        std::vector<pFunc2> pvcFunc2;
        std::vector<pFunc3> pvcFunc3;
    
    
        pvcFunc1.push_back(pFn1);
        pvcFunc1.push_back(pFn2);
    
        for (int i(0); i < pvcFunc1.size(); i++) {
            pvcFunc1[i]();
    
    }
    
    
    
    
        std::cout << std::endl << std::endl << std::endl;
        std::cin.get();
        return 0;
    }
    

答案 6 :(得分:0)

首先,我建议使用std::function而不是函数指针。它们更通用,可以用函数指针,函数对象或lambda表达式填充。典型用法如下:

{
  "CVE_data_type" : "CVE",
  "CVE_data_format" : "MITRE",
  "CVE_data_version" : "4.0",
  "CVE_data_numberOfCVEs" : "6208",
  "CVE_data_timestamp" : "2017-08-14T18:06Z",
  "CVE_Items" : [ {
    "cve" : {
      "CVE_data_meta" : {
        "ID" : "CVE-2003-1547"
      },
      "affects" : {
        "vendor" : {
          "vendor_data" : [ {
            "vendor_name" : "francisco_burzi",
            "product" : {
              "product_data" : [ {
                "product_name" : "php-nuke",
                "version" : {
                  "version_data" : [ {
                    "version_value" : "6.5"
                  }, {
                    "version_value" : "6.5_beta1"
                  }, {
                    "version_value" : "6.5_rc3"
                  }, {
                    "version_value" : "6.5_rc2"
                  }, {
                    "version_value" : "6.5_rc1"
                  } ]
                }
              } ]
            }
          } ]
        }
      },
      "problemtype" : {
        "problemtype_data" : [ {
          "description" : [ {
            "lang" : "en",
            "value" : "CWE-79"
          } ]
        } ]
      },
      "references" : {
        "reference_data" : [ {
          "url" : "http://secunia.com/advisories/8478"
        }, {
          "url" : "http://securityreason.com/securityalert/3718"
        }, {
          "url" : "http://www.securityfocus.com/archive/1/archive/1/316925/30/25250/threaded"
        }, {
          "url" : "http://www.securityfocus.com/archive/1/archive/1/317230/30/25220/threaded"
        }, {
          "url" : "http://www.securityfocus.com/bid/7248"
        }, {
          "url" : "https://exchange.xforce.ibmcloud.com/vulnerabilities/11675"
        } ]
      },
      "description" : {
        "description_data" : [ {
          "lang" : "en",
          "value" : "Cross-site scripting (XSS) vulnerability in block-Forums.php in the Splatt Forum module for PHP-Nuke 6.x allows remote attackers to inject arbitrary web script or HTML via the subject parameter."
        } ]
      }
    },
    "configurations" : {
      "CVE_data_version" : "4.0",
      "nodes" : [ {
        "operator" : "OR",
        "cpe" : [ {
          "vulnerable" : true,
          "cpeMatchString" : "cpe:/a:francisco_burzi:php-nuke:6.5",
          "cpe23Uri" : "cpe:2.3:a:francisco_burzi:php-nuke:6.5:*:*:*:*:*:*:*"
        }, {
          "vulnerable" : true,
          "cpeMatchString" : "cpe:/a:francisco_burzi:php-nuke:6.5_beta1",
          "cpe23Uri" : "cpe:2.3:a:francisco_burzi:php-nuke:6.5_beta1:*:*:*:*:*:*:*"
        }, {
          "vulnerable" : true,
          "cpeMatchString" : "cpe:/a:francisco_burzi:php-nuke:6.5_rc1",
          "cpe23Uri" : "cpe:2.3:a:francisco_burzi:php-nuke:6.5_rc1:*:*:*:*:*:*:*"
        }, {
          "vulnerable" : true,
          "cpeMatchString" : "cpe:/a:francisco_burzi:php-nuke:6.5_rc2",
          "cpe23Uri" : "cpe:2.3:a:francisco_burzi:php-nuke:6.5_rc2:*:*:*:*:*:*:*"
        }, {
          "vulnerable" : true,
          "cpeMatchString" : "cpe:/a:francisco_burzi:php-nuke:6.5_rc3",
          "cpe23Uri" : "cpe:2.3:a:francisco_burzi:php-nuke:6.5_rc3:*:*:*:*:*:*:*"
        } ]
      } ]
    },
    "impact" : {
      "baseMetricV2" : {
        "cvssV2" : {
          "vectorString" : "(AV:N/AC:M/Au:N/C:N/I:P/A:N)",
          "accessVector" : "NETWORK",
          "accessComplexity" : "MEDIUM",
          "authentication" : "NONE",
          "confidentialityImpact" : "NONE",
          "integrityImpact" : "PARTIAL",
          "availabilityImpact" : "NONE",
          "baseScore" : 4.3
        },
        "severity" : "MEDIUM",
        "exploitabilityScore" : 8.6,
        "impactScore" : 2.9,
        "obtainAllPrivilege" : false,
        "obtainUserPrivilege" : false,
        "obtainOtherPrivilege" : false,
        "userInteractionRequired" : true
      }
    },
    "publishedDate" : "2003-12-31T05:00Z",
    "lastModifiedDate" : "2017-08-08T01:29Z"
  } ]
}

但这并不能解决在std::vector中存储具有不同参数的函数的问题。由于它们具有不同的签名,因此您必须将它们视为不同的类型。与#include <iostream> #include <functional> struct Funktor { // This is a callable class/object void operator()() { std::cout << "Funktor called." << std::endl; } }; void function() { // Normal function std::cout << "Function called." << std::endl; }; int main() { std::function<void()> lambdaFunction = [](){ std::cout << "lambda function executed." << std::endl;}; // And a lambda expression (fancy way to write a function where you need it) std::function<void()> functionPointer = &function; std::function<void()> callableObject = Funktor(); //This is the way you call functions with a std::function object, just like with a normal function lambdaFunction(); functionPointer(); callableObject(); return 0; } int一样。

为了存储不同类型的元素,STL提供std::tuple。你可以用这个来实现你的目标。

std::string

如果您想同时实现两个,不同的参数和多个函数,您可以合并#include <iostream> #include <functional> #include <tuple> int main() { // std::tuple takes multiple template arguments. Each corresponds to one element in the tuple std::tuple< std::function<void()>, std::function<void(int)> > functionTuple; // To access a element of the tuple we call std::get<i> on the tuple // This will return a reference to the element in the tuple and we // can overwrite it with whatever we want std::get<0>(functionTuple) = [](){ std::cout << "Function without arguments." << std::endl; }; std::get<1>(functionTuple) = [](int arg){ std::cout << "Function without int as argument. Arg = " << arg << std::endl; }; // We use std::get to get the function and the call it. // The the trailing '()' and '(5)' are the actual function calls, // just like in the example above std::get<0>(functionTuple)(); std::get<1>(functionTuple)(5); // You can also use std::get<...> with a type as argument. // Have a look in the docs. Its a very nice feature of tuples return 0; } std::tuple

std::vector

所有人都说,我会加上一句谨慎的话。函数指针/对象和lambdas只是一个工具。它们非常灵活和强大,因此可能会导致您陷入意外行为和错误的兔子洞。如果您不打算编写非常通用的算法并深入研究模板元编程,那么这个工具很可能不是最好的工作。寻求像command pattern这样的不同解决方案可以让您的生活更轻松。

答案 7 :(得分:-1)

我不会这样做。 我不能告诉你是否可以在一个向量中使用这两个函数 - 我很确定它不是。

您应该创建一个类并使用对象向量。