【问题标题】:C++11 - What is wrong with this use of decltype function pointer?C++11 - 使用 decltype 函数指针有什么问题?
【发布时间】:2013-05-01 21:28:39
【问题描述】:

在尝试使用可变参数模板实现 Delegate 类时,我遇到了一个我无法解决的问题:

/// --------------------------------------
/// @thanks     God
///             Steve Reinalter
/// @author     Henri Korpela aka Helixirr
/// --------------------------------------
#include <cstdio>

template<typename>
class Delegate;

template<typename Return, typename Param, typename... ParamsOther>
class Delegate<Return (Param, ParamsOther...)>{
public:

    /// Constructors & destructors:
    Delegate(void) = default;
    Delegate(Delegate const& delegate_) = default;
    Delegate(Delegate&& delegate_) = default;

    /// Member functions:
    Delegate& bind(Return (*function_)(Param, ParamsOther...));
    template<class C>
    Delegate& bind(C& c_, Return (C::*function_)(Param, ParamsOther...));

    /// Member functions (overloaded operators, assignment):
    Delegate& operator=(Delegate const& delegate_) = default;
    Delegate& operator=(Delegate&& delegate_) = default;

    /// Member functions (overloaded operators, function call):
    inline Return operator()(Param param_, ParamsOther... params_other_) const;

private:
    /// Member data:
    Return (*_m_opFunction)(Param, ParamsOther...) = nullptr;
    void* _m_opInstance = nullptr;
    /// Static member functions:
    template<class C, Return (C::*Function)(Param, ParamsOther...)>
    static inline Return _wrap_function_member(void* instance_, Param param_, ParamsOther... params_other_);
    template<Return (*Function)(Param, ParamsOther...)>
    static inline Return _wrap_function_static(void*, Param param_, ParamsOther... params_other_);
};

/// Member functions:
template<typename Return, typename Param, typename... ParamsOther>
Delegate<Return (Param, ParamsOther...)>& Delegate<Return (Param, ParamsOther...)>::bind(Return (*function_)(Param, ParamsOther...)){
    _m_opFunction = &_wrap_function_static<decltype(function_)>;
    _m_opInstance = nullptr;
    return *this;
}
template<typename Return, typename Param, typename... ParamsOther>
template<class C>
Delegate<Return (Param, ParamsOther...)>& Delegate<Return (Param, ParamsOther...)>::bind(C& c_, Return (C::*function_)(Param, ParamsOther...)){
    _m_opFunction = &_wrap_function_member<C, decltype(function_)>;
    _m_opInstance = &c_;
    return *this;
}
/// Member functions (overloaded operators, function call):
template<typename Return, typename Param, typename... ParamsOther>
Return Delegate<Return (Param, ParamsOther...)>::operator()(Param param_, ParamsOther... params_other_) const{
    return _m_opFunction(_m_opInstance, param_, params_other_...);
}
/// Static member functions:
template<typename Return, typename Param, typename... ParamsOther>
template<class C, Return (C::*Function)(Param, ParamsOther...)>
Return Delegate<Return (Param, ParamsOther...)>::_wrap_function_member(void* instance_, Param param_, ParamsOther... params_other_){
    return (static_cast<C*>(instance_)->*Function)(param_, params_other_...);
}
template<typename Return, typename Param, typename... ParamsOther>
template<Return (*Function)(Param, ParamsOther...)>
Return Delegate<Return (Param, ParamsOther...)>::_wrap_function_static(void*, Param param_, ParamsOther... params_other_){
    return (Function)(param_, params_other_...);
}

int f(int i_){
    return i_ * 2;
}

int main(void){
    Delegate<int (int)> delegate__;
    delegate__.bind(&f);
    printf("Result: %i\n", delegate__(8));
    return 0;
}

我尝试使用 C++11 编译器 (GCC 4.7.2) 在 Ideone 上编译它,但似乎失败了:

prog.cpp: 在'Delegate& Delegate::bind(Return (*)(Param, ParamsOther ...)) 的实例化中 [with Return = int;参数 = 整数;参数其他 = {}]': prog.cpp:79:23:从这里需要 prog.cpp:45:5: 错误: 没有匹配转换函数‘_wrap_function_static’到类型‘int (*)(int)’ prog.cpp:39:26: 错误: 候选是: 模板静态返回委托::_wrap_function_static(void*, Param, ParamsOther ...) [with Return (* Function)(Param, ParamsOther ...) = Function;返回 = 整数;参数 = 整数;参数其他 = {}] prog.cpp:在“Return Delegate::operator()(Param, ParamsOther ...) const [with Return = int;参数 = 整数;参数其他 = {}]': prog.cpp:80:40: 从这里需要 prog.cpp:59:65:错误:从“void*”到“int”的无效转换 [-fpermissive] prog.cpp:59:65: 错误:函数参数太多

据我了解,decltype函数指针在这里

template<typename Return, typename Param, typename... ParamsOther>
Delegate<Return (Param, ParamsOther...)>& Delegate<Return (Param, ParamsOther...)>::bind(Return (*function_)(Param, ParamsOther...)){
    _m_opFunction = &_wrap_function_static<decltype(function_)>;
    _m_opInstance = nullptr;
    return *this;
}

似乎是导致问题的原因。当我尝试将成员函数绑定到委托时也会发生同样的情况。为什么会这样?我究竟做错了什么?对我来说,获取函数指针的类型并将该类型用作模板参数似乎很自然,但由于某种原因,它在这里不起作用。 这个 decltype 和函数指针场景有什么问题?

【问题讨论】:

  • 完全不清楚您要做什么。您将函数指针传递给bind,但bind 从不使用其参数的值。您是否计划以后能够调用传递的函数?
  • template&lt;Return (*Function)(Param, ParamsOther...)&gt; static inline Return _wrap_function_static /*...*/ 期望函数指针作为非类型模板参数。 &amp;_wrap_function_static&lt;decltype(function_)&gt; 提供函数指针类型作为模板参数。
  • 哦,顺便说一句。为什么不使用std::bindstd::function
  • 我不认为你可以将你的 bind 和你的包装机制结合起来:bind 没有函数指针作为非类型模板参数,因此只能访问运行时指针。您的包装机制需要在编译时访问指针(以实例化适当的包装器)。您可以用检查 _m_opInstance == nullptr 替换包装(本质上只区分成员函数和非成员函数),但 IMO 的设计可以说是有缺陷的。
  • @DyP:“bind 没有函数指针作为非类型模板参数,因此只能在运行时访问指针”。现在我看到了问题,模板无法在运行时解析,只能在编译时解析。悲伤,但可以理解的原因。但是为什么编译器没有检测到这个错误而不是“没有匹配转换函数'_wrap_function_static'到类型'int(*)(int)'?编译器在这里很难弄清楚吗?顺便说一句,为什么要你认为这个设计有缺陷吗?

标签: c++ c++11 function-pointers member-function-pointers decltype


【解决方案1】:

这是来自 clang++ 3.2 的错误消息的第一部分:

temp.cpp:41:19: error: assigning to 'int (*)(int)' from incompatible type
      '<overloaded function type>'
    _m_opFunction = &_wrap_function_static<decltype(function_)>;
                  ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
temp.cpp:75:16: note: in instantiation of member function 'Delegate<int
      (int)>::bind' requested here
    delegate__.bind(&f);
               ^
temp.cpp:35:26: note: candidate function has different number of parameters
      (expected 1 but has 2)
    static inline Return _wrap_function_static(void*, Param param_,...
                         ^


temp.cpp:55:41: error: too many arguments to function call, expected 1, have 2
    return _m_opFunction(_m_opInstance, param_, params_other_...);
           ~~~~~~~~~~~~~                ^~~~~~
temp.cpp:76:38: note: in instantiation of member function 'Delegate<int
      (int)>::operator()' requested here
    printf("Result: %i\n", delegate__(8));

这是因为_m_opFunction的声明:

Return (*_m_opFunction)(Param, ParamsOther...) = nullptr;

template<Return (*Function)(Param, ParamsOther...)>
static inline Return _wrap_function_static(void*, Param param_, ParamsOther... params_other_);

也就是说,_wrap_function_static 需要 void* 然后将参数转发给函数调用,而 _m_opFunction 只需要函数调用的参数。


标准库解决方案:

#include <iostream>
#include <functional>

int f(int i_){
    return i_ * 2;
}

struct foo
{
    int m;
    int f(int i) { return i * m; }
};

int main()
{
    std::function<int (int)> delegate__;

    delegate__ = f;
    std::cout << "Result: " << delegate__(8) << std::endl;

    foo o;
    o.m = 21;
    delegate__ = std::bind(&foo::f, std::ref(o), std::placeholders::_1);
    std::cout << "Result: " << delegate__(2) << std::endl;
}

尝试修正您的方法: 注意:您不能从成员函数指针转换为“普通”函数指针(它可以使用联合或复制原始数据...... UB)。更好的方法是使用多态性(即虚函数和调用者对象的动态分配)。

#include <cstdio>

template<typename>
class Delegate;

template<typename Return, typename Param, typename... ParamsOther>
class Delegate<Return (Param, ParamsOther...)>{
public:

    /// Constructors & destructors:
    Delegate(void) = default;
    Delegate(Delegate const& delegate_) = default;
    Delegate(Delegate&& delegate_) = default;

    /// Member functions:
    Delegate& bind(Return (*function_)(Param, ParamsOther...));
    template<class C>
    Delegate& bind(C& c_, Return (C::*function_)(Param, ParamsOther...));

    /// Member functions (overloaded operators, assignment):
    Delegate& operator=(Delegate const& delegate_) = default;
    Delegate& operator=(Delegate&& delegate_) = default;

    /// Member functions (overloaded operators, function call):
    inline Return operator()(Param param_, ParamsOther... params_other_) const;

private:
    /// Member data:
    Return (*_m_opFunction)(Param, ParamsOther...) = nullptr;
    Return (Delegate::*_m_opMemFunction)(Param, ParamsOther...) = nullptr;
    void* _m_opInstance = nullptr;

    /// function wrappers:
    template<class C>
    static inline Return _wrap_member_function(Delegate const&, Param param_, ParamsOther... params_other_);
};

/// Member functions:
template<typename Return, typename Param, typename... ParamsOther>
Delegate<Return (Param, ParamsOther...)>& Delegate<Return (Param, ParamsOther...)>::bind(Return (*function_)(Param, ParamsOther...)){
    _m_opFunction = function_;
    _m_opMemFunction = nullptr;
    _m_opInstance = nullptr;
    return *this;
}
template<typename Return, typename Param, typename... ParamsOther>
template<class C>
Delegate<Return (Param, ParamsOther...)>& Delegate<Return (Param, ParamsOther...)>::bind(C& c_, Return (C::*function_)(Param, ParamsOther...)){
    _m_opFunction = reinterpret_cast<decltype(_m_opFunction)>( &_wrap_member_function<C> );
    _m_opMemFunction = reinterpret_cast<decltype(_m_opMemFunction)>( function_ );
    _m_opInstance = &c_;
    return *this;
}
/// Member functions (overloaded operators, function call):
template<typename Return, typename Param, typename... ParamsOther>
Return Delegate<Return (Param, ParamsOther...)>::operator()(Param param_, ParamsOther... params_other_) const{
    if(nullptr == _m_opMemFunction)
    {
        return _m_opFunction(param_, params_other_...);
    }else
    {
        auto f = reinterpret_cast<Return (*)(Delegate const&, Param, ParamsOther...)>( _m_opFunction );
        return f(*this, param_, params_other_...);
    }
}
/// function wrappers:
template<typename Return, typename Param, typename... ParamsOther>
template<class C>
Return Delegate<Return (Param, ParamsOther...)>::_wrap_member_function(Delegate<Return (Param, ParamsOther...)> const& instance_, Param param_, ParamsOther... params_other_){
    Return (C::*memFuncPtr)(Param, ParamsOther...) = reinterpret_cast<decltype(memFuncPtr)>( instance_._m_opMemFunction );
    return (reinterpret_cast<C*>(instance_._m_opInstance)->*memFuncPtr)(param_, params_other_...);
}

int f(int i_){
    return i_ * 2;
}

struct foo
{
    int m;
    int f(int i) { return i * m; }
};

int main(void){
    Delegate<int (int)> delegate__;
    delegate__.bind(&f);
    printf("Result: %i\n", delegate__(8));

    foo o;
    o.m = 21;
    delegate__.bind(o, &foo::f);
    printf("Resilt: %i\n", delegate__(2));
    return 0;
}

为什么我说“设计有缺陷”:

  • 标准库已包含“委托”类型 (std::function)
    • StdLib 绑定​​中成员函数的参数顺序不同(std::bind(&amp;A::func, instance)delegate__.bind(instance, &amp;A::func)
    • StdLib 绑定​​复制实例,您必须显式使用std::ref(或指针)来传递引用
  • 函数调用复制参数(更好:完美转发)
  • 为什么要拆分ParamParamOthers中的参数?
  • 与仿函数不兼容

【讨论】:

  • 非常感谢,我现在明白了。但是,我不是已经将函数指针默认初始化为 0 了吗?就像在“Delegate(void) = default;”中一样?我还在类范围内初始化了 fptrs。我错过了什么吗?好吧,无论如何,有很多演员表,但演员表基本上就是这一切的意义所在。我喜欢 std::function 和 std::bind 示例,帮助我比较不同的方法,即使我认为我的语法对用户更友好。不过,必须添加仿函数和 lambda 支持。我会看看情况如何,如果出现问题,您可能会发现我再次在这里提问。 :D
  • @Helixirr 如果你不喜欢它们的语法,你可以包装 std::functionstd::bind。 Ad init of fct ptrs: [class.ctor]/6 “隐式定义的默认构造函数执行类的初始化集,这些初始化将由用户编写的没有 ctor-initializer 的类的默认构造函数执行”(相同对于显式默认的 ctor) 没有大括号或等号初始化程序的非静态数据成员,并且也不会出现在 ctor-initializer 中,它们将是默认初始化程序;但您需要 value-init (-> zero-init) 将 fct ptrs 设置为 nullptr
  • 不知道为什么函数指针初始化是这样的。如果它按照我的方式工作不是更有意义吗?是的,感谢您的提示,包装器可能是个好主意。
  • @Helixirr 这与函数指针无关。没有显式初始化程序的非类类型的每个数据成员(intboolmy_type*,...)根本不会使用 default-init 进行初始化,您无需付费对于你不需要/使用的东西。这些类型的值初始化会导致零初始化,但这需要显式初始化器(如my_type::my_type() : int_member{}
猜你喜欢
  • 2012-10-16
  • 1970-01-01
  • 2012-10-28
  • 1970-01-01
  • 2016-05-31
  • 1970-01-01
  • 2012-06-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多