我试图学习如何在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个整数。
如何将两个函数存储在同一个向量中?
答案 0 :(得分:4)
你想要的既不可能也不合理。这是不可能的,因为键入了函数指针,并且指向void(int, int)
的指针与指向void(int)
的指针的类型不同。 vector
是同类容器;它的所有元素必须是同一类型。这两种类型是无关的;你不能将指向一种类型的指针转换成指向另一种类型的指针,并期望调用它。
您可以做的最好的事情是使用指向不同函数类型的variant
指针。现在,我不知道你将如何调用这些函数,因为不同的类型采用不同的参数列表。你怎么能通过visit
或仿函数来调用它?您是否有足够的参数转发到相关功能?如果没有,那么重点是什么?
除非您事先知道列表中的索引X具有特定的参数列表,并且您将这些参数传递给它,否则没有有效的方法来调用它。如果您确实知道这一点,那么您可能需要的是tuple
或struct
函数指针,而不是它们的运行时容器。
答案 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)
我不会这样做。 我不能告诉你是否可以在一个向量中使用这两个函数 - 我很确定它不是。
您应该创建一个类并使用对象向量。