模板特化的隐式实例化

3
我正在设计一个类模板Monad,该模板由一个模板类型参数化。例如,可以有Monad<std::vector>Monad<std::shared_ptr>等。
我有一个工具struct,可用于提取高阶类型的容器类型(例如,从std::vector<int>中提取std::vector):
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

现在谈到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";
    }
};

我正在尝试让Monad::foo自动推断其模板参数。因此,不需要执行以下操作:

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);

我希望能够做到以下几点:

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);

我得到的错误信息(两个未编译的示例都是如此)为:
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();
        ~^~

我该如何解决这个问题?

更新: 正如Sam所指出的,std::vectorFType<std::vector<Something>>::typeFType<std::vector<SomethingElse>>::type都是不同的!(尽管对于特定的Astd::is_same表明std::vector<A>FType<std::vector<Something>>::type<A>FType<std::vector<SomethingElse>>::type<A>是相同的)。如果我专门为Monad<FType<std::vector<int>>::type>进行特化,那么事情就能解决...但很明显这是不可取的...有没有其他方法来实现FType

更新2:FType更改为以下内容:

template<typename... A>
struct FType<std::vector<A...>> {
    template<typename... B>
    using type = std::vector<B...>;
};

没有任何效果。


@SamVarshavchik 我正在使用-std=c++11 -Wall -Wextra -pedantic-errors,g++ 4.8/4.9/5.3和Xcode 7.3附带的clang++都可以编译它(在OS X上)。也许您没有使用C++11? - Zizheng Tai
如果你的评论是针对我的,我正在使用c++11模式下的gcc 5.3.1。较新版本的gcc在强制执行C++方面更加严格。此外:Monad专业化需要一个std::vector作为参数。这与Ftype... :: type的结果不匹配。我认为这样做行不通。 - Sam Varshavchik
为了使“这个版本可行”,我必须将std::move(v)作为参数传递给foo函数,这是有道理的。 - Sam Varshavchik
我相信因为对于“这个有效”的情况,F 明确地作为一个 std::vector。对于不起作用的情况,您将 F 解析为基本上是 template<typename B> class std::vector。这两者并不相同。 - Sam Varshavchik
@Useless 我已经更新了FType,但它仍然无法工作。请参见上面的更新。 - Zizheng Tai
显示剩余3条评论
1个回答

2

为什么它不起作用

问题出在当你明确地提供std::vectorF时,F实际上是std::vector。但是当你允许它被推导时,它被推导为FType<A>::type - 这不是std::vector。它可以被推导为这样,但它并不相同。

如何修复

实际上,对于这个特定问题,你不需要任何额外的机制,对吧?你只需要将foo的参数推导为模板模板的一个实例:

template <template <typename...> class F, typename... Args>
static void foo(F<Args...> const& ) {
    using M = Monad<F>; 
    M::bark();
}

更普遍地说,你可以通过单一步骤从 FA 转换为单子:
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>;
};

然后:

template <class FA>
static void foo(FA const& ) {
    using M = as_monad_t<FA>; 
    M::bark();
}

实际问题更加复杂。基本上,我有一个类型为F<A>(例如std::vector<int>),一个可调用类型Fn,并且std::result_of<Fn(A)>::type给出了一个G<B>(我将其作为整体返回,GB)。问题是我无法从GB中分离出G;我需要G来找到专门的Monad实现。 - Zizheng Tai
@ZizhengTai 只需通过类型特征一步到位 - Barry
谢谢!这正是我想要的! - Zizheng Tai

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接