Следующий код не работает, потому что предполагаемый параметр шаблона F равен std::tuple
, тогда как я хочу, чтобы он был Foo
— первый принимает два параметра шаблона, а второй — один.
#include <tuple>
template <typename T>
using Foo = std::tuple<int, T>;
template <template <typename> class F>
void foo(F<std::string> bar) {}
void test() {
foo(Foo<std::string>());
}
Есть ли способ заставить вывод типа работать с оператором using
, а не превращать Foo
в свой собственный класс?
#include <tuple>
template <typename T>
class Foo {
std::tuple<int, T> bar;
};
template <template <typename> class F>
void foo(F<std::string> bar) {}
void test() {
foo(Foo<std::string>());
}
Больше информации
Я использую std::variant
С++ 17 вместе с псевдонимами типов, которые являются общими для одного типа, и я бы предпочел объявлять их с помощью операторов using
, а не создавать классы-оболочки для каждого из них. Что-то вроде этого:
// Assuming Plus, Minus, etc all exist
template <typename T>
using Operation = std::variant<Plus<T>, Minus<T>, Times<T>>;
Создание функтора в стиле Haskell
Целью этого упражнения является создание небольшой библиотеки функторов, свободно основанной на классе типов функторов Haskell. Я определил «класс типов» следующим образом:
template <template <typename> class F>
class Functor {
public:
template <typename T, typename U>
static F<U> fmap(std::function<U(T)> f, F<T> functor);
};
Но я также хотел добавить немного сахара, чтобы вы могли создать общий преобразователь, который будет отображать функцию на любой тип функции без предварительного указания типа функтора:
template <typename T, typename U>
struct FMap {
FMap(std::function<U(T)> f) : f_(f) {}
template <template <typename> class F>
F<U> operator()(F<T> functor) {
return Functor<F>::fmap(f_, functor);
}
private:
std::function<U(T)> f_;
};
template <typename T, typename U>
FMap<T, U> fmap(std::function<U(T)> f) {
return FMap<T, U>(f);
}
Это хорошо работает с простым функтором-оболочкой значений:
template <typename T>
class Value {
public:
Value(T value) : value_(value) {}
const T& value() const {
return value_;
}
private:
T value_;
};
template <>
template <typename T, typename U>
Value<U> Functor<Value>::fmap(std::function<U(T)> f, Value<T> value) {
return Value<U>(f(value.value()));
}
void test() {
std::function<std::string(int)> fn = [](int x) {
return std::to_string(x);
};
auto result = fmap(fn)(Value(42));
// result.value() == "42"
}
Теперь я пытаюсь заставить его работать с более сложным типом, который использует std::tuple
или std::variant
, как в приведенном выше примере.
template <>
template <typename T, typename U>
Foo<U> Functor<Foo>::fmap(std::function<U(T)> f, Foo<T> value) {
return Foo<U>(std::get<0>(value), f(std::get<1>(value)));
}
void test() {
std::function<std::string(int)> fn = [](int x) {
return std::to_string(x);
};
// This is the desirable syntax but it doesn't build
// fmap(fn)(Foo<int>(42, 7));
// This builds but it's super ugly
fmap(fn).operator()<Foo>(Foo<int>(42, 7));
}
Основываясь на ответе SkepticalEmpiricist ниже, я думаю, что псевдонимы типов не могут быть здесь, и вместо этого мне придется ввести небольшие классы-оболочки - если только не будет подхода SFINAE, который заставит это работать.
Эта библиотека в основном представляет собой любопытство и средство для меня, чтобы изучить некоторые более продвинутые концепции шаблонов - спасибо за помощь!