我有这个示例(我想在模板C
中添加模板重载的构造函数:
#include <iostream>
#include <functional>
#include <string>
using namespace std;
class Msg {
public:
void parse(const string &s) {
x = int(s[0]);
}
int x = 0;
};
class C {
public:
C(function<void(const string &)> cb)
: cb_(cb)
{}
template<typename TMsg>
C(function<void(const TMsg &)> cb)
: C([&, cb](const string &s) {
TMsg m; m.parse(s); cb(m);
})
{}
void test() {
cb_("foo");
}
function<void(const string &)> cb_;
};
void cb1(const string &s) {
cout << "cb1: " << s << endl;
}
void cb2(const Msg &msg) {
cout << "cb2: Msg{x=" << msg.x << "}" << endl;
}
int main() {
C c1(&cb1), c2(&cb2); c1.test(); c2.test();
return 0;
}
出现此编译器错误(clang ++ 9.1):
test_func_arg_overload.cpp:37:17: error: no matching constructor for initialization of 'C'
C c1(&cb1), c2(&cb2); c1.test(); c2.test();
^ ~~~~
test_func_arg_overload.cpp:12:7: note: candidate constructor (the implicit move constructor) not
viable: no known conversion from 'void (*)(const Msg &)' to 'C' for 1st argument
class C {
^
test_func_arg_overload.cpp:12:7: note: candidate constructor (the implicit copy constructor) not
viable: no known conversion from 'void (*)(const Msg &)' to 'const C' for 1st argument
class C {
^
test_func_arg_overload.cpp:14:5: note: candidate constructor not viable: no known conversion from
'void (*)(const Msg &)' to 'function<void (const std::__1::string &)>' (aka 'function<void (const
basic_string<char, char_traits<char>, allocator<char> > &)>') for 1st argument
C(function<void(const string &)> cb)
^
test_func_arg_overload.cpp:18:5: note: candidate template ignored: could not match 'function<void
(const type-parameter-0-0 &)>' against 'void (*)(const Msg &)'
C(function<void(const TMsg &)> cb)
^
1 error generated.
除了使C
为模板类之外,还有其他方法可以解决此问题吗?
答案 0 :(得分:2)
您只需要将函数指针转换为正确的类型。这应该起作用:
using f_t = std::function<void(const Msg &)>;
C c2(static_cast<f_t>(&cb2));
问题是,没有强制转换,您将提供函数指针,该函数指针应自动转换为std::function<>
,如果您对确切类型的构造函数有重载,则可以使用,如第一个示例所示。
但是在第二种情况下,重载分辨率与模板版本不匹配,因为类型不匹配并且不执行类型推导。
答案 1 :(得分:1)
lambda和函数指针与std::function
不匹配。
您可以将代码更改为
template <typename F,
std::enable_if_t<std::is_same<std::string,
std::decay_t<arg1_trait_t<F>>>::value, int> = 0>
C(F cb)
: cb_(cb)
{}
template <typename F,
std::enable_if_t<!std::is_same<std::string,
std::decay_t<arg1_trait_t<F>>>::value, int> = 0>
C(F cb) : C([cb](const string &s) {
std::decay_t<arg1_trait_t<F>> m;
m.parse(s);
cb(m);
})
{}
使用
template <typename T> struct arg1_trait : arg1_trait<decltype(&T::operator())> {};
template <typename Ret, typename Arg>
struct arg1_trait<Ret (*) (Arg)> : arg1_trait<Ret (Arg)> {};
template <typename Ret, class C, typename Arg>
struct arg1_trait<Ret (C::*) (Arg)> : arg1_trait<Ret (Arg)> {};
template <typename Ret, class C, typename Arg>
struct arg1_trait<Ret (C::*) (Arg) const> : arg1_trait<Ret (Arg)> {};
template <typename Ret, typename Arg>
struct arg1_trait<Ret (Arg)>
{
using type = Arg;
};
template <typename T>
using arg1_trait_t = typename arg1_trait<T>::type;