何时删除模板实例比删除非模板重载更可取?

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

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

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

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

void processPointer(void*) = delete;

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

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

声明非模板重载更容易(没有与尖括号混在一起)。是否有理由我更喜欢删除模板实例化呢?

1
+1,因为我自己也不知道答案。我会删除模板实例化,因为我倾向于将整个重载集合视为模板。当以后阅读代码时,这样做会更容易理解发生了什么(即遵循最小惊奇原则)。我不知道选择一种方式还是另一种方式是否有技术上的原因。 - utnapistim
4个回答

25

3
+1是在泛型代码中明确指定模板参数非常常见的写法。 - Daniel Frey

4
这可能会给你一些启示:
#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的答案正确。

2
你认为这可能会给出什么样的见解?(我认为我知道,但由于你实际上没有说,我不确定。请写一两句话重申代码应该说明什么。) - Rob Kennedy
1
template<> void X::processPointer<void>(void*) = delete; 或者没有参数推导的写法 template<> void X::processPointer(void*) = delete; 可以在类范围之外进行特化. - Casey

4
我不认为需要在这里使用模板
实际上,通过删除非模板重载,您可能会避免一些我现在想不到的边缘情况下的歧义调用,因为非模板优先于模板实例化。因此,在大多数情况下使其按预期工作。

0
假设您想将类型为void*(或简单地使用nullptr)的参数pointer传递给您的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;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接