使用模板化可变参数模板参数作为专用参数

Posted

技术标签:

【中文标题】使用模板化可变参数模板参数作为专用参数【英文标题】:Use a templated variadic template parameter as specialized parameter 【发布时间】:2014-04-16 20:15:13 【问题描述】:

我的标题可能是错误的 - 如果是这样,请纠正我,但在某些时候我很难跟踪我实际想要实现的元目标;)

我有一个这样的类函数模板:

template<template<typename...> class MapType>
    Expression Expression::substitute(MapType<std::string, Expression> const& identifierToExpressionMap) const 
        return SubstitutionVisitor<MapType>(identifierToExpressionMap).substitute(something);
    

重要的部分是 MapType。这个想法是允许随意插入std::mapstd::unordered_map。使用 GCC 和 Clang,这是可行的,但 Visual Studio 2013 会引发编译错误:

error C2664: 'Expression Expression::substitute<:map>(const MapType &) const' :

无法将参数 1 从 'std::map<:string>,std::allocator<:pair _kty>>>' 转换为 'const std::地图 &'

1> 与
1> [
1> MapType=std::map
1>]
1> 和
1> [
1> _Kty=std::字符串
1> , _Ty=表达式
1>]

1> 原因:无法从 'std::map<:string>,std::allocator<:pair _kty>>>' 转换为 'const std ::地图'

1> 与
1> [
1> _Kty=std::字符串
1> , _Ty=表达式
1>]

1> 没有可以执行此转换的用户定义转换运算符,或者无法调用该运算符

MSVC 好像没有把MapType&lt;std::string, Expression&gt; 作为一个类型放在一起,我是不是漏掉了什么?

所以我的问题是

    这是否可能,只是 MSVC 中的一个错误,或者 有没有更简单的方法来做到这一点。请记住,还有许多其他类接收MapType 参数并使用不同的键/值类型实例化它们自己的版本。

【问题讨论】:

当 MSVC 不同意 GCC 和 Clang 时,这是 MSVC 中的一个错误。 不幸的是,我现在没有 VS2013 可以测试,但由于回复不多,因此您可以测试以下内容:充实函数替代上的参数,使其具有 2 个默认参数地图的模板(std::less 和 std::allocator)。然后让substitute 的调用者尝试传入一个指定了所有模板参数的映射(即:它具有std::less 和std::allocator)。这里的测试是看如果所有模板参数在每一步都显式存在,VS是否处理得更好。 @qeadz 但是 unordered_map 的模板参数比 map 多 - 我可以 not 传递 std::map 并将所有参数指定为替换,因为它不再是模板并且我在类中需要不同的键/值类型 - 也许我不明白你的建议......? @qeadz 我想你提到了一个重要的问题:std::map 类模板实际上有四个模板参数,所以当用作模板时,它应该需要 template-argument 所有四个模板参数来实例化(因为默认参数没有传递给模板模板参数)。然而,一些例子用 clang++ 和 g++ 编译,不知道为什么。 哇...这似乎是一个悬而未决的问题:open-std.org/JTC1/SC22/WG21/docs/cwg_active.html#150 【参考方案1】:

大概是这样的:

template<class T, class U = int>
struct cat ;

template< template<class...> class Animal >
void foo()

    Animal<int> x; (void)x; // (A)


int main()

    foo<cat>();

这个看似无辜的小程序被clang++和g++接受,但不被MSVC2013 Update 1接受。


行 (A) 是有问题的:我认为在这个例子中,模板 template-parameter Animal 在传递类模板 cat 时应该有两个模板参数。

clang++ 和 g++ 似乎支持在 (A) 行中为此模板 template-parameter 使用默认模板参数,而 MSVC 不支持。

我不知道标准是否需要其中任何一个;见例子

this answer by Richard Smith CWG active issue #150

在我看来,支持默认模板参数当然很有用,因为(正如在活动问题中所说)标准库的类模板可能有额外的模板参数(带有默认参数);如果您想(直接)将它们用作模板template-arguments,则需要了解这些实现细节。

【讨论】:

【参考方案2】:

鉴于已经发生的讨论,我认为也许是时候考虑解决方法了。由于推断模板模板参数似乎是手头的问题,也许放宽对 MapType 的要求可能会有所帮助。

template< typename MapType>
Expression Expression::substitute( MapType const& identifierToExpressionMap) const 
    return SubstitutionVisitor<MapType>(identifierToExpressionMap).substitute(something);

调用者将被编译为调用函数的代码,因此编译器将推导出模板参数,因此基本上您将使用正确的 std::map 或 std::unordered_map 的责任推给调用者。

现在至少在大多数情况下这可能有效。但是,可以传入某种可以编译但实际上没有正确类型的容器。所以理想情况下,您仍然需要某种编译时检查以确保支持 MapType(即:std::map 或 std::unordered_map)。

这可以通过 Boost 概念,或者甚至只有两个模板化别名声明 - 使用 enable_if 可以确保别名声明仅在两种风格中可用:map 或 unordered_map。

【讨论】:

我接受了另一个答案,因为那里描述了实际的原因和问题,所以对于任何有类似问题的人来说,我可能会更有用。很抱歉,你们都给出了很好的见解。

以上是关于使用模板化可变参数模板参数作为专用参数的主要内容,如果未能解决你的问题,请参考以下文章

可变参数模板错误:“在实例化中”(gcc 9.2)

如何将构造函数(可变参数)作为模板参数传递?

为啥这个嵌套的可变参数模板是无效参数?

匹配任何类型参数的 C++ 可变参数模板模板参数

第20课 可变参数模板_模板参数包和函数参数包

每个可变参数模板参数生成一个类成员