什么时候删除模板实例比删除非模板重载更可取?

Posted

技术标签:

【中文标题】什么时候删除模板实例比删除非模板重载更可取?【英文标题】:When is deleting a template instantiation preferable to deleting a non-template overload? 【发布时间】:2014-01-28 11:19:02 【问题描述】:

假设我有一个使用原始指针的模板:

template<typename T>
void processPointer(T* ptr);

我不希望使用void* 指针调用它。看来我有两个选择。我可以删除非模板重载:

void processPointer(void*) = delete;

或者我可以删除一个模板实例化:

template<>
void processPointer<void>(void*) = delete;

声明非模板重载更容易(不用尖括号)。有什么理由让我更喜欢删除模板实例化吗?

【问题讨论】:

+1,因为我自己也不知道答案。我会删除模板实例化,因为我倾向于将整个重载集视为模板。我认为稍后在阅读代码时会更容易理解发生了什么(即遵循最小意外原则)。我不知道选择一种方式是否有技术原因。 【参考方案1】:

Here's one reason 支持模板版本:processPointer&lt;void&gt;(void*) 仍然可以直接调用,避免其他重载。

【讨论】:

+1 因为显式指定模板参数在通用代码中很常见。【参考方案2】:

我看不出有什么理由在这里做模板

事实上,通过删除非模板重载,您可能会摆脱一些我现在无法想到的边缘情况模棱两可的调用,因为非模板优先于模板实例化。从而在大多数情况下使这项工作如愿以偿。

【讨论】:

【参考方案3】:

这可能会提供见解:

#include <iostream>

struct X

    template<typename T>
    void processPointer(T* ptr) 
        std::cout << "Template\n";
    

    // error: explicit specialization in non-namespace scope ‘struct X’
    // template<>
    // void processPointer(void*) = delete;

    // Overload but no specialization
    // This will prevent lookup the specialization outside the class, when no
    // template argument is explicitly given.  However, with an explicit 
    // template argument the specialization is called.
    void processPointer(void*) = delete;
;

// Specialization outside the class body
template<>
void X::processPointer(void* ptr) 
    std::cout << "Specialization\n";


int main ()

    X x;
    //error: use of deleted function ‘void X::processPointer(void*)’
    //x.processPointer((void*)0);

    // Explicit template argument:
    x.processPointer<void>((void*)0);

结论:@Casey 的答案成立。

【讨论】:

您认为这可能会带来什么见解? (我认为我知道,但是由于您实际上没有说,我不确定。请写一两句话来重申代码应该说明的内容。) template&lt;&gt; void X::processPointer&lt;void&gt;(void*) = delete;或不带参数推导出template&lt;&gt; void X::processPointer(void*) = delete;can be specialized outside the class scope。【参考方案4】:

假设您想将 void* 类型的参数 pointer(或简单的 nullptr)传递给您的 processPointer 函数,并且您还想调用其对 Type 类型的特化。那你应该写

processPointer(static_cast<Type>(pointer));

void processPointer(void*) = delete;

但是对于

template<>
void processPointer<void>(void*) = delete;

您可以编写更短的代码:

processPointer<Type>(pointer);

所以这两种变体都可以在不同的情况下使用。

但是,在某些情况下,具有非模板重载的变体的类似物可能是唯一的方法。 假设有一个带有两个参数的函数模板:

template<typename T, typename U>
void processPointer(T* ptr1, U* ptr2);

您不希望使用void* 指针作为第一个参数来调用它。 C++ 中不允许函数模板的部分特化,所以这段代码不正确:

template<typename U>
void processPointer<void, U>(void*, U*) = delete;

而且你必须使用另一个:

template<typename U>
void processPointer(void*, U*) = delete;

【讨论】:

以上是关于什么时候删除模板实例比删除非模板重载更可取?的主要内容,如果未能解决你的问题,请参考以下文章

C++ Primer 5th笔记(chap 16 模板和泛型编程)重载与模板

C++初阶模板

放大:如何删除 Auth 并使用 Api 资源添加新的 Auth 资源。错误模板错误:Fn::GetAtt 实例引用未定义资源

C++模板进阶

”更高级的宏“模板到底是怎么实例化的?

”更高级的宏“模板到底是怎么实例化的?