В Haskell классы типов позволяют элегантно перегружать функции в зависимости от типа возвращаемого значения. Реплицировать это в C ++ довольно просто для случаев, когда оба аргумента и возвращаемый тип перегружены с использованием шаблонов (пример A):
template <typename In, typename Out> Out f(In value);
template <typename T> int f<T, int>(T value) {
...
}
Что соответствует Хаскеллу:
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 и C ++ с помощью перегруженной функции. interpretValue
это автоматически выталкивает или конвертирует из стека Lua. Для простых типов, имеющих прямое встроенное представление Lua, это достаточно просто с помощью кода, такого как пример B.
Для более сложных типов я также пишу template <typename T> struct Data
чтобы управлять управлением памятью для объектов (мост между GC Lua и C ++ стороны рефконт), и я надеялся, что смогу перегрузить interpretValue
так что он может автоматически обернуть указатель пользовательских данных в Data<T>
, Я попытался использовать следующее, но clang выдал ошибку «вызов функции неоднозначен»:
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);
}
Затем вам нужно написать этот тип возвращаемого значения с помощью операторов приведения, так что вы получите то, что хотите:
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
Объявления s предназначены для того, чтобы немного усложнить сохранение результата этой функции и использовать его позже — как здесь:
auto c = interpretValue<float>(state, 1);
float& d = c; // not compile
Нужно использовать std::move
так как &&
означает, что функция может использоваться только для ссылок rvalue:
auto c = interpretValue<float>(state, 1);
float& d = std::move(c);
Других решений пока нет …