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

时间:2011-07-09 09:35:55

标签: c++ templates

我想将一个函数作为模板参数传递给另一个函数,以便以后可以存储和调用它。在某些情况下,我想为回调传递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;
}

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

有没有办法解决这个问题,或者做一些棘手的模板专业化或其他一些聪明的事情来获得理想的效果?

编辑:

以上是一个简化的例子,说明了我遇到的问题,但这是我试图解决的具体问题。我有this project我正在努力。这是一组使C ++和Lua混合更简单的函数(出于各种原因,我不想使用LuaBind或我发现的其他现有函数)。这个问题的重要功能是luaW_register<T>,靠近底部。这是一个稍微过时的版本,但它几乎适用于所有情况。但是,如果构造函数是私有的,那么它就不起作用了,当我尝试将它与Box2D的b2Body混合时(这需要从b2World制作)。 luaW_defaultallocator<T>()(和luaW_defaultdeallocator<T>())仍然被创建,因为我将它用作luaW_register<T>()中的默认参数。

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

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

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似乎更清晰。

5 个答案:

答案 0 :(得分:3)

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

如何使事情有效:似乎你选择了错误的工具,无论你想要达到什么目的。

干杯&amp;第h。,

答案 1 :(得分:3)

这可以通过使用模板重载来解决。而不是只有一个'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情况。

答案 2 :(得分:1)

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

(Foo*)(*make_null_foo)() = 0;

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

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

或者您可以执行注释建议的内容,并通过创建一个返回null的实际函数来简化逻辑,而不是使用null函数指针的特殊大小写:

Foo* make_null_foo() { return 0; }

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

即。空对象模式。

答案 3 :(得分:0)

这很难看,但确实有效:

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

答案 4 :(得分:0)

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

无论如何你可以这样做:

template <typename T, T* (*func)()>
T* T_new() {
   return func();
}
template <typename T>
T* T_new() {
   return 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;
}