在C ++中复制Haskell的返回类型重载(通过类型类)

时间:2015-11-12 19:37:59

标签: c++ templates haskell overloading typeclass

在Haskell中,类型类允许您根据返回类型优雅地重载函数。使用模板(示例A)重载参数和返回类型的情况下,在C ++中复制它是微不足道的:

template <typename In, typename Out> Out f(In value);

template <typename T> int f<T, int>(T value) {
   ...
}

这与Haskell的对应:

class F a b where
    f :: a -> b

你甚至可以在大多数函数上重载返回类型(例子B):

template <typename Out> Out f(SomeClass const &value);

template <> inline int f(SomeClass const &value) {
    return value.asInt();
}

template <> inline float f(SomClass const &value) {
    return value.asFloat();
}

对应于以下内容:

class F a where
    f :: SomeData -> a

但我希望能够做的是修改最后一个示例以重载高阶类型,即C ++中的模板化结构。也就是说,我希望能够编写类似于以下Haskell的专业化:

data Foo a = Foo a

instance F (Foo a) where
    f someData = Foo $ ...

如何使用此功能编写模板(甚至可能)?

作为参考,我打算用它来编写Lua / C ++桥的模板函数。这个想法是通过一个自动推送或转换Lua堆栈的重载函数interpretValue来桥接Lua和C ++函数。对于具有直接内置Lua表示的简单类型,使用代码(如示例B)很容易。

对于更复杂的类型,我还写了template <typename T> struct Data来处理对象的内存管理(在Lua的GC和C ++端引用之间桥接),我希望能够重载interpretValue以便它可以自动将userdata指针包装到Data<T>中。我尝试使用以下内容,但是clang给了一个&#34;函数调用是不明确的&#34;错误:

template <typename U> inline U &interpretValue(lua_State *state, int index) {
    return Data<U>::storedValueFromLuaStack(state, index);
}

template <typename U> inline Data<U> interpretValue(lua_State *state, int index) {
    return Data<U>::fromLuaStack(state, index);
}

谢谢!

1 个答案:

答案 0 :(得分:2)

好吧,你可以编写一个函数:

template <class U>
interpretValueReturnType<U> interpretValue(lua_State *state, int index)
{
    return interpretValueReturnType<U>(state, index);
}

然后,您需要使用强制转换操作符编写此返回类型,因此,您可以获得所需内容:

template <class U>
class interpretValueReturnType
{
public:
    interpretValueReturnType(lua_State *state, int index) : state(state), index(index) {}

    operator U& () &&
    {
        return Data<U>::storedValueFromLuaStack(state, index);
    }

    operator Data<U> () &&
    {
        return Data<U>::fromLuaStack(state, index);
    }
private:
    lua_State *state;
    int index;
};

请参阅ideone

int main() {
    lua_State *state;
    int& a = interpretValue<int>(state, 1);
    Data<int> b = interpretValue<int>(state, 1);
}

&&声明末尾的这个有趣的operator是为了稍微难以存储此函数的结果并在以后使用它 - 就像这里:

    auto c = interpretValue<float>(state, 1);
    float& d = c; // not compile

需要使用std::move,因为&&表示该函数只能用于右值引用:

    auto c = interpretValue<float>(state, 1);
    float& d = std::move(c);