模板专门化的隐式实例化

Implicit instantiation of template specialization

本文关键字:实例化 专门化      更新时间:2023-10-16

我正在设计一个类模板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...>;
};

没有效果。

为什么它不起作用

问题是,当您显式地为F提供std::vector时,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转到monad:

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