具有编译时间常数的模板特化

Posted

技术标签:

【中文标题】具有编译时间常数的模板特化【英文标题】:Template specialization with compile time constant 【发布时间】:2014-08-18 11:55:14 【问题描述】:

我正在尝试为具有编译时间常数的模板类构建特化。

模板类如下所示:

template<class TNativeItem, class TComItem = void,
         VARTYPE _vartype = _ATL_AutomationType<TComItem>::type>
class InOutComArray

private:
    CComSafeArray<TComItem, _vartype> _safeArray;
    // ...
public:
    InOutComArray(
        TNativeItem* items, size_t length,
        std::function<TComItem(const TNativeItem&)> convertToCom,
        std::function<TNativeItem(const TComItem&)> convertFromCom)
        : _safeArray(length)
    
        // ...
    

    // ...
;

用法例如:

InOutComArray<BOOL, VARIANT_BOOL, VT_BOOL>(
    items, length, BOOLToVARIANT_BOOL, VARIANT_BOOLToBOOL));

但是,也存在不需要转换的类型,我想为此提供一个简写版本:

InOutComArray<LONG>(items, length);

我尝试这样实现它:

template<class TItem, VARTYPE _vartype = _ATL_AutomationType<TItem>::type>
class InOutComArray<TItem, void, _vartype>
    : public InOutComArray<TItem, TItem, _vartype>

public:
    InOutComArray(TItem* items, size_t length)
        : InOutComArray<TItem, TItem, _vartype>(
              items, length, NoConverter<TItem>, NoConverter<TItem>)
    

    
;

但是,我收到以下错误:

'_vartype' : 部分特化不允许使用默认模板参数

有什么办法吗?

【问题讨论】:

报错信息很清楚。 @40two 你的评论没有帮助。我专门询问了一种绕过该错误消息的方法。 【参考方案1】:

您首先将默认参数定义为void_ATL_AutomationType&lt;TComItem&gt;::type,因此当只给出一个参数X 时,您希望InOutComArray&lt;X&gt; 成为InOutComArray&lt;X, void, _ATL_AutomationType&lt;void&gt;::type&gt;

您的部分专业化与此相矛盾:InOutComArray&lt;X&gt; 应为InOutComArray&lt;X, X, _ATL_AutomationType&lt;X&gt;::type&gt;

根据您认为第二个参数更可能是什么(即void 或与第一个参数相同),您可以首先将第二个参数默认为第一个:

template<class TNativeItem, class TComItem = TNativeItem,
     VARTYPE _vartype = _ATL_AutomationType<TComItem>::type>

这样就覆盖了部分特化的行为,除了额外的构造函数。这可以通过使用构造函数的默认参数来实现:

template<class TNativeItem, class TComItem = TNativeItem,
     VARTYPE _vartype = _ATL_AutomationType<TComItem>::type>
class InOutComArray

public:
InOutComArray(
    TNativeItem* items, size_t length,
    std::function<TComItem(const TNativeItem&)> convertToCom = NoConverter<TNativeItem>(),
    std::function<TNativeItem(const TComItem&)> convertFromCom = NoConverter<TNativeItem>());
;

【讨论】:

非常感谢您提供这么好的信息!这看起来很优雅,但是,以下情况并不能解决:InOutComArray&lt;LONG, VT_I8&gt;(items, length)。有没有办法允许这样做? std::enable_if 似乎不起作用,我收到了'type' : is not a member of 'std::enable_if&lt;false,size_t&gt;'。 Looks like不能这样用吗? 一个InOutComArray&lt;LONG, VT_I8&gt; 将是一个InOutComArray&lt;LONG, VT_I8, _ATL_AutomationType&lt;VT_I8&gt;::type&gt; - 我想这不是你想要的? (不知道 VT_I8 可能是什么)如果您正在寻找“重载”第二个模板参数,那么您可以拥有 TComItem 或 VARTYPE(无论是什么)。我不确定这是否可能 - 您必须在其中一种情况下指定所有参数。 我的意思是我可以写InOutComArray&lt;LONG, VT_I8&gt; 并得到InOutComArray&lt;LONG, LONG, VT_I8&gt; @DanielHilgarth:更正了 enable_if 部分,不需要它。至于您的最后一条评论:您可以将InOutComArray&lt;X, Y&gt; 解析为InOutComArray&lt;X, Y, _ATL_AutomationType&lt;Y&gt;::type&gt;InOutComArray&lt;X, X, Y&gt;,但有时不是前者,有时不是后者。你似乎两者都想要——你必须做出决定。【参考方案2】:

根据标准§14.5.5/8 Class template partial specializations [temp.class.spec]:

特化的模板参数列表不应包含默认模板参数值。

因此,编译器理所当然地抱怨,因为在您的部分特化中,您为 VARTYPE _vartype = _ATL_AutomationType&lt;TItem&gt;::type 提供了默认模板参数值。

【讨论】:

感谢您提供有关评论的详细信息。但我并没有真正问编译器是否正确。【参考方案3】:

有什么办法吗?

是的,从部分特化中删除默认模板参数。你不需要它。

根据主模板:

template<class TNativeItem, class TComItem = void,
         VARTYPE _vartype = _ATL_AutomationType<TComItem>::type>
class InOutComArray

这些类型是等价的:

InOutComArray<LONG>
InOutComArray<LONG, void, _ATL_AutomationType<TComItem>::type>

每当InOutComArrayTComItem = void 实例化时,您都会得到部分特化:

template<class TItem, VARTYPE _vartype>
class InOutComArray<TItem, void, _vartype>

【讨论】:

哈,确实。但是,以下情况并不能由此解决:InOutComArray&lt;LONG, VT_I8&gt;(items, length)。有没有办法允许这样做? 这不是专业化就能解决的。特化不会引入另一种提供模板参数的方式。

以上是关于具有编译时间常数的模板特化的主要内容,如果未能解决你的问题,请参考以下文章

C++模板详解:泛型编程模板原理非类型模板参数模板特化分离编译

C++初阶:模板进阶非类型模板参数 | 模板的特化 | 模板分离编译

C++初阶:模板进阶非类型模板参数 | 模板的特化 | 模板分离编译

C++初阶:模板进阶非类型模板参数 | 模板的特化 | 模板分离编译

C++模板进阶操作 —— 非类型模板参数模板的特化以及模板的分离编译

C++模板进阶(非类型模板参数类模板的特化和模板的分离编译)