无法将空函数指针作为模板参数传递

Unable to pass null function-pointer as template argument

本文关键字:参数传递 指针 函数      更新时间:2023-10-16

我想将一个函数作为模板参数传递给另一个函数,以便它可以存储并在以后调用。在某些情况下,我想为回调传递NULL,但我遇到了麻烦。下面是我希望能够做到的一个例子:

#include <iostream>
struct Foo {
    int i;
};
template <typename T>
T* T_new() {
    return new T();
}
Foo* Foo_new() {
    return new Foo();
}
template <typename T, T* (*func)()>
T* T_new() {
    if (func)
        return func();
    else
        return NULL;
}
int main(void) {
    // Works
    Foo* f1 = T_new<Foo>();
    std::cout << f1 << std::endl;
    // Works
    Foo* f2 = T_new<Foo, Foo_new>();
    std::cout << f2 << std::endl;
    // fails to compile, "no matching function for call to ‘T_new()’"
    // Foo* f3 = T_new<Foo, NULL>();
    // std::cout << f3 << std::endl;
    return 0;
}

我发现了这个类似的问题,但是处理传递null作为构造函数的参数,而不是传递null作为模板参数,并且那里的技巧(使用(Foo*)0)不作为模板参数工作。

是否有一种方法可以解决这个问题,或者做一些棘手的模板专门化或其他一些聪明的事情来获得所需的效果?

编辑:

上面是一个简单的例子,说明了我遇到的问题,但这里是我试图解决的具体问题。我正在做一个项目。这是一组函数,使我更容易混合使用c++和Lua(出于各种原因,我不想使用LuaBind或我发现的其他现有函数)。这个问题的重要函数是luaW_register<T>,在底部附近。这是一个有点过时的版本,但它几乎适用于所有情况。它不起作用,但是,如果构造函数是私有的,当我尝试将它与Box2D的b2Body(需要由b2World制成)混合时,它刚刚出现。luaW_defaultallocator<T>()(和luaW_defaultdeallocator<T>())仍然得到创建,因为我使用它作为默认参数在luaW_register<T>()

我提出的解决方案是将allocator参数拉到luaW_Register的模板参数中。然后,如果我想使用其他函数来获取特定类型的对象,luaW_defaultallocator甚至不会被创建。在像b2Body s这样的情况下,他们根本无法创建自己,我希望能够将NULL作为模板参数传递(这似乎是完全合理的,但是编译器因为我仍然不清楚的原因而窒息,似乎如果我可以在代码中的其他任何地方设置NULL的值,我应该也能够为模板设置)。黑客我最初实现是通过在一个布尔参数函数将禁用调用Foo.new从Lua代码的能力,但这并不阻止defaultallocator编译,如果我可以使用null检查和工作方式我想它有不错的副作用让我简单地检查是否有一个分配器和用它来控制是否new函数添加到Lua表。

tl;dr:我的目标是从这里开始:

template <typename T>
void luaW_register(lua_State* L, const char* classname, const luaL_reg* table, const luaL_reg* metatable, const char** extends = NULL, bool disablenew = false, T* (*allocator)() = luaW_defaultallocator<T>, void (*deallocator)(T*) = luaW_defaultdeallocator<T>)

:

template <typename T, T* (*allocator)() = luaW_defaultallocator<T>, void (*deallocator)(T*) = luaW_defaultdeallocator<T> >
void luaW_register(lua_State* L, const char* classname, const luaL_reg* table, const luaL_reg* metatable, const char** extends = NULL)

以避免在某些情况下实例化luaW_defaultallocator,但看起来这可能不可能。

到目前为止,我所见过的最接近的解决方案是提供一个像luaW_cannotalloc<T>(lua_State*)这样的函数,它返回NULL,可以在我的luaW_register函数中检查,而不是NULL。我想这可以工作,但这意味着更多的输入和需要记住函数名,NULL似乎更干净。

这可以通过使用模板重载来解决。而不是只有一个' T_new '签名,你将有一个签名用于NULL情况,一个用于其他情况:

// Unused signature, no implementation so using this will result in link error
template<typename T, typename F>
T* T_new();
// NULL overload (NULL is an int)
template<typename T, int func>
T* T_new()
{
    assert(func == 0 && "Signature should only be used with NULL");
    return NULL;
}
// Valid function pointer overload
template<typename T, T* (*func)()>
T* T_new()
{
    // I don´t think it´s possible with NULL functions now, but if it is
    // we'll handle that too
    if (func)
        return func();
    return NULL;
}

技巧在于认识到NULL实际上是一个int,并使用它来处理不同重载中的NULL情况。

空指针的问题是模板指针实参必须具有外部链接。null没有链接

如何让事情工作:不管你想要达到什么目的,似乎你选择了错误的工具。

干杯,hth。

你可以(我认为)设置一个适当类型的常量并使用它:

(Foo*)(*make_null_foo)() = 0;
Foo* f3 = T_new<Foo, make_null_foo>();

或者在c++ 0x中,您应该能够使用新的nullptr关键字。

或者您可以按照注释所建议的那样做,并通过创建一个返回null的实际函数来简化逻辑,而不是为空函数指针进行特殊的大小写:

Foo* make_null_foo() { return 0; }
Foo* f3 = T_new<Foo, make_null_foo>();

看起来很丑,但是很好用:

Foo* f3 = T_new<Foo, (Foo* (*)())NULL>();

我真的不认为模板在这里有意义,我不确定你是否真的想过这个…为什么你要静态地调用一个函数,知道它将返回NULL?

无论如何,你可以这样做:

template <typename T, T* (*func)()>
T* T_new() {
   return func();
}
template <typename T>
T* T_new() {
   return NULL;
}

或者如果您需要通过中间模板(例如,您可能不知道在给定的点上函数是否将null,您可以遵循null对象模式并提供null函数:

template <typename T>
T* null_new() { return 0; }
template <typename T, T* (*f)() >
T* T_new() {
   return f();
}
// user code:
X* p = T_new<X, null_new >();

或者,忘记使用函数指针作为模板的实参,而将其作为实参传递给函数:

template <typename T>
T* T_new( T* (*func)() = 0 ) {
    if (func)
        return func();
    else
        return NULL;
}
相关文章: