C++ 模板模板参数可以接受采用非类型参数的模板吗?

问题描述 投票:0回答:3

我有一个这样的函数来为C++实现

fmap

// Given a mapping F from T to U and a container of T, return a container of U
// whose elements are created by the mapping from the original container's
// elements.
template <typename F, template <typename...> typename Container, typename T>
Container<std::invoke_result_t<F&, const T&>> Fmap(F&& f,
                                                   const Container<T>& input);

这个想法是使用模板模板参数 (

Container
) 来允许接受任何类似 STL 的容器。我尝试过的实际 STL 中的所有内容都工作正常,但我们的代码库中的自定义容器不起作用,因为它接受非类型模板参数

template <typename Key, int Foo = 256>
class MyContainer;

这会导致 clang 替换失败:

template template argument has different template parameters than its corresponding template template parameter

有没有一种方法可以抽象所有模板参数,而不仅仅是类型?如果没有,是否有更好的方法来构建我的代码,以允许做我想做的事情,而无需专门针对

MyContainer
和所有其他类似的东西特别吗?

c++ templates c++17 c++20 template-templates
3个回答
3
投票

一个模板模板参数只能匹配一种模板;那种类型是由模板参数列表决定的。如果你想接受

Fmap
,你必须写另一个版本的
MyContainer
。但是,如果这样做,您可以匹配具有一个类型参数后跟任意数量的非类型参数的任何模板:它可以是像示例中那样的
int
,也可以是
char
bool 
...

template <typename F, template <typename, auto...> typename Container, typename T, auto ...Vs>
Container<std::invoke_result_t<F&, const T&>, Vs...> Fmap(F&& f, const Container<T, Vs...>& input) {
    return {};
}

演示


0
投票

由于您的函数不知道模板模板参数的类型,并且无法使用它们,因此它们都必须在实际模板参数中具有默认值。您可以通过创建和使用别名模板来利用这一事实:

template <typename F, template <typename /* no pack */> typename Container, typename T>
Container<std::invoke_result_t<F&, const T&>> Fmap(F&& f,
                                                   const Container<T>& input);

template <typename X> using MyContainerDefault =  MyContainer<X>;

something = Fmap(someFunction, MyContainerDefault);

话虽如此,标准库算法不接受或返回容器是有充分理由的。有很多资源对此进行了解释,只需搜索

why stl algorithms do not work with containers

值得一提的另一点是,在 Haskell 中,每个“容器”(函子)都以自己的方式实现

fmap
,因此
Fmap
的通用实现相当可疑。如果你想克隆
fmap
,它应该为每个容器都有一个重载或者是一个容器的成员函数。一旦接受这一点,您就不再需要模板模板参数,因为每个容器都知道自己的模板参数。


0
投票

为了补充已经给出的一般建议(每个容器的超载),我认为您可以通过探索Louis Dionne如何完成这件事,对一种方式有一个很好的概述Boost.Hana 定义了

Functor
和其他概念(是的,还有
Applicative
Monad
Comonad
等),以及
boost::hana::basic_tuple
等类型或
boost::hana::optional
(编译时可选)实现它
。关于您的问题,值得阅读的有趣部分可能是标签调度这里是如何将
std::array
制作为
hana::Functor
的完整示例,以便您可以在其上应用
hana::transform

另一个可以拓宽您对这个主题的看法的有趣读物是P1895,它也链接到Barry Revzin关于该主题的帖子(不完全赞成链接提案中的

tag_invoke
方法)是关于)。

前段时间,我也问过这个话题的问题,但是还没有得到满意的答复。

© www.soinside.com 2019 - 2024. All rights reserved.