请考虑以下C ++程序:
#include <cstdlib>
#include <functional>
#include <iostream>
#include <string>
#include <utility>
namespace {
template <typename Result, typename... Arg>
Result call_fn(std::function<Result(Arg...)> fn, Arg&&... arg) {
return fn(std::forward<Arg>(arg)...);
}
std::string test_fn(int, std::string*) {
return "hello, world!";
}
}
int main(int, char**) {
std::cout << call_fn(std::function(test_fn), 0, nullptr) << "\n";
return EXIT_SUCCESS;
}
这是一个人为的示例,但是在尝试实现与std::make_unique
类似的东西时遇到了相同的问题。编译该程序时,出现以下错误:
$ clang++ -std=c++17 -Wall -Weverything -Werror -Wno-c++98-compat call_fn.cpp
call_fn.cpp:19:18: error: no matching function for call to 'call_fn'
std::cout << call_fn(std::function(test_fn), 0, nullptr) << "\n";
^~~~~~~
call_fn.cpp:9:8: note: candidate template ignored: deduced conflicting types for parameter 'Arg' (<int, std::__cxx11::basic_string<char> *> vs. <int, nullptr_t>)
Result call_fn(std::function<Result(Arg...)> fn, Arg&&... arg) {
^
1 error generated.
问题似乎是指针参数的类型推导为nullptr_t
,而不是std::string*
。我可以通过添加static_cast
或为call_fn
显式指定模板参数来“修复”此问题,但是我发现这种冗长的级别令人讨厌。
是否有一种方法可以修改call_fn
的定义,以便类型推导对指针参数更有效?
答案 0 :(得分:1)
问题在于,一方面Arg...
定义了std::function<...>
的推导规则,另一方面,输入参数定义了它。
您应该只使用std::invoke
而不是call_fn
或使用typename... Args2
作为输入参数。或者,您可以放弃输入为std::function
的要求,而只接受任何可调用对象。
另一种选择是确保输入参数不用于参数类型推导,但我不确定如何使用可变参数模板(typename...
)-对语法有疑问。
编辑:让我写一个例子
template<typename T> foo(std::vector<T> x, T y);
std::vector<double> x;
int y;
foo(x,y);
由于冲突,此处foo
将无法推断T
。解决此问题的方法之一是确保类型推导中不使用y
。
template<typename T>
struct same_type_impl { type=T;};
template<typename T>
using same_type = typename same_type_impl<T>::type;
template<typename T> foo(std::vector<T> x, same_type<T> y);
std::vector<double> x;
int y;
foo(x,y);
这里foo
会推论T
为double
。
答案 1 :(得分:0)
根据我的测试,我认为std::function
是罪魁祸首。对可变参数模板的类型太严格了(我认为)。
以下作品:
#include <iostream>
#include <string>
#include <utility>
namespace {
template <typename FnT, typename... Arg>
auto call_fn(FnT fn, Arg&&... arg) {
return fn(std::forward<Arg>(arg)...);
}
std::string test_fn(int, std::string*) { return "hello, world!"; }
} // namespace
int main(int, char**) {
std::cout << call_fn(test_fn, 0, nullptr) << "\n";
return EXIT_SUCCESS;
}
像这样,我们只是模板化std::function
,现在唯一的要求是我们提供的函数接受这些参数并且可以调用(operator()
)。