我正在开发具有以下结构的程序:
#include <iostream>
#include <string>
void fun(const std::string &text, int a, int b) { // (1)
std::cout << text << a + b << std::endl;
}
template<typename ...Args>
void execute(void(*fun)(Args...), Args ...args) {
fun(args...);
}
void init(const std::string &text, int a, int b) {
execute(fun, text, a, b);
}
int main() {
init("Fun: ", 1, 2);
return 0;
}
我收到错误消息
.code.tio.cpp:14:2: error: no matching function for call to 'execute'
execute(fun, text, a, b);
^~~~~~~
.code.tio.cpp:9:6: note: candidate template ignored: deduced conflicting types for parameter 'Args' (<const std::__cxx11::basic_string<char> &, int, int> vs. <std::__cxx11::basic_string<char>, int, int>)
void execute(void(*fun)(Args...), Args ...args) {
^
1 error generated.
我可以通过删除(1)行中的引用来修正错误:
void fun(const std::string text, int a, int b) {
但是我想通过引用而不是通过值传递值。功能模板
template<typename ...Args>
void execute(void(*fun)(Args...), Args ...args)
不得更改。我该如何解决这个问题,以使text
通过引用传递,execute
保持不变,并且init
也保持不变?
编辑:
@super表明我错了,必须重新制定要求。 execute
只能在不依赖此功能的其他项目中断的范围内进行修改。我没有想到这样的解决方案。
答案 0 :(得分:4)
建议:使用两组模板可变参数
template <typename ... As1, typename ... As2>
void execute(void(*fun)(As1...), As2 ... args) {
fun(args...);
}
这样,您可以在fun()
函数参数中保留引用,并将其传递给字符串值。
更多一般而言:这是一场噩梦,它推论出该函数的参数集与以下参数完全相同。没必要。
假设您有一个foo()
函数,它接收到一个long
void foo (long)
{ }
然后调用execute()
,并传递一个foo()
指针和一个int
execute(foo, 1);
如果您使用单个Args...
可变参数序列,则调用将按照您的问题而失败,因为编译器将Args...
推导出为long
(来自foo()
签名)和{{ 1}}(来自值long
),因此模棱两可。
如果使用两个可变参数序列,则编译器为1
推论long
,为As1...
推论int
,没有歧义,并且As2...
传递了一个为需要execute()
值的函数提供int
值,这是完全合法的。
答案 1 :(得分:3)
我认为您无需更改execute
,而无需触摸init()
。一种方法是显式传递模板参数(绕过参数推导以便传输引用类型信息):
void init(const std::string &text, int a, int b) {
execute<const std::string&>(fun, text, a, b);
}
答案 2 :(得分:3)
我不确定您为什么不想更改execute
,但我认为最好将其修改为对可调用对象使用单独的模板参数。
这具有附加的优点,您可以传递任何可调用对象,例如lambda或std::function
或函子。
添加完美的转发是另一个好主意。可调用对象可以说是转发得尽可能通用。
#include <utility>
template<typename F, typename ...Args>
void execute(F fun, Args&& ...args) {
fun(std::forward<Args>(args)...);
}
如果函数的签名很重要,这就是为什么您不想修改execute
的原因,那么有多种方法可以从具有特征类型的F
中提取出来。
答案 3 :(得分:1)
它不起作用,因为其中一个参数为const&
-您可能已经注意到了。通过创建一个包含const引用的辅助结构可以消除这些关键字:
#include <iostream>
#include <string>
#include <functional>
template<typename T>
struct const_ref {
const_ref(const T& value) : value(value) {}
const std::reference_wrapper<const T> value;
};
void fun(const_ref<std::string> text, int a, int b) {
std::cout << text.value.get() << a + b << std::endl;
}
template<typename ...Args>
void execute(void(*fun)(Args...), Args ...args) {
fun(args...);
}
void init(const std::string &text, int a, int b) {
const_ref<std::string> refstring{ text };
execute(fun, refstring, a, b);
}
int main() {
init("Fun: ", 1, 2);
}
这样extecute()
不会改变。维护起来也不难,因为可以简单地将const T&
声明为const_ref<T>
。