Reputation: 6646
I'm designing a class template Monad
, which is parameterized by a template type. For example, there can be Monad<std::vector>
, Monad<std::shared_ptr>
, etc.
I have this utility struct
that can be used to extract the container type of a higher-kinded type (e.g. extract std::vector
from a std::vector<int>
):
template<typename>
struct FType;
template<typename A>
struct FType<std::vector<A>> {
template<typename B>
using type = std::vector<B>;
};
// FType<std::vector<int>>::type == std::vector
Now for Monad
:
template<template <typename...> class F>
struct Monad;
template<>
struct Monad<std::vector> {
/*
* Example parameters:
* FA: std::vector<int>
* F: std::vector
* M: Monad<std::vector>
*/
template<typename FA,
template <typename...> class F = FType<FA>::template type,
typename M = Monad<F>>
static void foo(const FA &)
{
M::bark();
}
static void bark()
{
std::cout << "Woof!\n";
}
};
I'm trying to have Monad::foo
automatically infer its template parameters. So instead of doing:
std::vector<int> v{1, 2, 3};
// This works
Monad<std::vector>::foo<
std::vector<int>, // FA
std::vector, // F
Monad<std::vector> // M
>(v);
// This also works
Monad<std::vector>::foo<
std::vector<int>, // FA
std::vector // F
>(v);
I want to be able to do:
std::vector<int> v{1, 2, 3};
// This does not compile
Monad<std::vector>::foo(v);
// Neither does this
Monad<std::vector>::foo<
std::vector<int> // FA
>(v);
The error I got (for both not-compiling examples) was:
error: implicit instantiation of undefined template 'Monad<type>'
M::bark();
^
template is declared here
struct Monad;
^
error: incomplete definition of type 'Monad<type>'
M::bark();
~^~
How can I solve this?
UPDATE: As Sam has pointed out, std::vector
, FType<std::vector<Something>>::type
, and FType<std::vector<SomethingElse>>::type
are all different! (Although for a specific A
, std::is_same
shows that std::vector<A>
, FType<std::vector<Something>>::type<A>
, and FType<std::vector<SomethingElse>>::type<A>
are the same.) If I specialize Monad<FType<std::vector<int>>::type>
, then things work out...but clearly this is not desirable... Is there any other way to implement FType
?
UPDATE 2: Changing FType
to this:
template<typename... A>
struct FType<std::vector<A...>> {
template<typename... B>
using type = std::vector<B...>;
};
has no effect.
Upvotes: 3
Views: 683
Reputation: 304122
The problem is when you explicitly provide std::vector
for F
, F
is actually std::vector
. But when you allow it to be deduced, it gets deduced as FType<A>::type
- which is not std::vector
. It's deducible as such, but it isn't the same.
You don't actually... need any of this extra machinery for this particular problem right? You can just deduce the argument to foo
as an instance of a template template:
template <template <typename...> class F, typename... Args>
static void foo(F<Args...> const& ) {
using M = Monad<F>;
M::bark();
}
More generally, you can just go from FA
to the monad in a single step:
template <class FA>
struct as_monad;
template <class FA>
using as_monad_t = typename as_monad<FA>::type;
template <template <typename...> class F, typename... Args>
struct as_monad<F<Args...>> {
using type = Monad<F>;
};
And then:
template <class FA>
static void foo(FA const& ) {
using M = as_monad_t<FA>;
M::bark();
}
Upvotes: 2