使用function <>参数时,模板ctor类型推导不起作用(没有匹配的构造函数用于...的初始化)

时间:2018-07-19 11:17:02

标签: c++

我有这个示例(我想在模板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为模板类之外,还有其他方法可以解决此问题吗?

2 个答案:

答案 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;

http://localhost/xyz/home/xyz/profile