在 C++ 中复制 Haskell 的 Return 类型重载(通过类型类)
Replicate Haskell's Return Type Overloading (via Typeclasses) in C++
在 Haskell 中,类型类允许您优雅地重载基于 return 类型的函数。对于参数和 return 类型都重载的情况,使用模板(示例 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
您甚至可以在大多数函数上仅重载 return 类型(示例 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++ 桥编写模板函数。这个想法是通过重载函数 interpretValue
在 Lua 和 C++ 函数之间架起桥梁,自动从 Lua 堆栈压入或转换。对于具有直接内置 Lua 表示的简单类型,使用示例 B 等代码就足够简单了。
对于更复杂的类型,我还写了一个 template <typename T> struct Data
来处理对象的内存管理(桥接 Lua 的 GC 和 C++ 端引用计数),我希望能够重载 interpretValue
以便它可以自动将用户数据指针包装到 Data<T>
中。我尝试使用以下内容,但 clang 给出了 "function call is ambiguous" 错误:
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);
}
谢谢!
嗯,你可以写一个函数:
template <class U>
interpretValueReturnType<U> interpretValue(lua_State *state, int index)
{
return interpretValueReturnType<U>(state, index);
}
然后,您需要使用转换运算符编写此 return 类型,因此,您会得到想要的结果:
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);
在 Haskell 中,类型类允许您优雅地重载基于 return 类型的函数。对于参数和 return 类型都重载的情况,使用模板(示例 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
您甚至可以在大多数函数上仅重载 return 类型(示例 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++ 桥编写模板函数。这个想法是通过重载函数 interpretValue
在 Lua 和 C++ 函数之间架起桥梁,自动从 Lua 堆栈压入或转换。对于具有直接内置 Lua 表示的简单类型,使用示例 B 等代码就足够简单了。
对于更复杂的类型,我还写了一个 template <typename T> struct Data
来处理对象的内存管理(桥接 Lua 的 GC 和 C++ 端引用计数),我希望能够重载 interpretValue
以便它可以自动将用户数据指针包装到 Data<T>
中。我尝试使用以下内容,但 clang 给出了 "function call is ambiguous" 错误:
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);
}
谢谢!
嗯,你可以写一个函数:
template <class U>
interpretValueReturnType<U> interpretValue(lua_State *state, int index)
{
return interpretValueReturnType<U>(state, index);
}
然后,您需要使用转换运算符编写此 return 类型,因此,您会得到想要的结果:
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);