函数模板专业化失败?

发布于 2024-12-28 16:37:58 字数 728 浏览 2 评论 0原文

#include <iostream>

template <class T> 
void foo(T) {
    std::cout << "foo(T)" << std::endl;
}

template <class T> 
void foo(T*) { //#3
    std::cout << "foo(T*)" << std::endl;
}

#define TEST

#ifdef TEST
template <> 
void foo(int*) { //#1
    std::cout << "foo(int*)" << std::endl;
}
#else
template <>
void foo<int*>(int*) { //#2
    std::cout << "foo<int*>(int*)" << std::endl;
}
#endif

int main(int argc, char **argv) {
    int* p = 0;
    foo(p);
    return 0;
}

上面的#1#2有什么区别?如果我定义 TEST,则 #1 有效,但如果我将其注释掉,#3 有效。在这里编写函数模板专业化的正确方法是什么?

#include <iostream>

template <class T> 
void foo(T) {
    std::cout << "foo(T)" << std::endl;
}

template <class T> 
void foo(T*) { //#3
    std::cout << "foo(T*)" << std::endl;
}

#define TEST

#ifdef TEST
template <> 
void foo(int*) { //#1
    std::cout << "foo(int*)" << std::endl;
}
#else
template <>
void foo<int*>(int*) { //#2
    std::cout << "foo<int*>(int*)" << std::endl;
}
#endif

int main(int argc, char **argv) {
    int* p = 0;
    foo(p);
    return 0;
}

What is the difference between #1 and #2 above? If I define TEST, then #1 works, but if I comment it out, #3 works. Which is the right way to write function template specialization here?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

尹雨沫 2025-01-04 16:37:58

#1 声明 #3 的函数模板特化并自动推导模板参数。 #2 是您为 T=int* 定义的第一个模板(没有编号的模板,我们称之为 #0)的特化。它不能是 #3 的特化,因为用指定的 int* 替换 T 会导致出现 int** 参数。

当您调用 foo 时,重载解析现在首先选择最合适的基本模板,然后检查该模板是否有任何现有的专业化。定义了 TEST 后,有两个基本模板(#0 和 #3),#3 更匹配并被选中。然后编译器检查该模板的特殊化,#1 更适合并且正在被调用。

如果没有定义 TEST,仍然有两个基本模板(#0 和 #3),并且 #3 是更好的匹配并被选中。然后编译器检查该模板的特化,但由于 #2 特化 #0 而不是 #3,因此不考虑它,并且 #3 结束调用。

这是为什么不专门化函数模板的经典示例。那里对问题进行了更详细的解释。

简单的解决方案是根本不专门化函数模板,而只是为特殊类型添加新的重载:

// no template, just a normal function
void foo(int*) {
    std::cout << "foo(int*)" << std::endl;
}

#1 declares a function template specialization of #3 and automatically deduces the template parameters. #2 is a specialization of the first template you defined (the one without number, let's call it #0) for T=int*. It can't be a specialization of #3 because replacing T with the specified int* there would lead to a int** parameter.

When you call foo, overload resolution now first picks the best fitting base template, then checks that template for any existing specializations. With TEST defined, there are two base templates (#0 and #3) and #3 is a better match and gets selected. Then the compiler checks for specializations of that template, and #1 is a better fit and is being called.

Without TEST defined, there are still two base templates (#0 and #3) and #3 is a better match and gets selected. Then the compiler checks for specializations of that template, but since #2 specializes #0 and not #3, it is not considered and #3 ends of being called.

This is the classical example of Why not Specialize Function Templates. The problems are explained in more detail there.

The simple solution is to not specialize function templates at all, but simply add new overloads for the special types:

// no template, just a normal function
void foo(int*) {
    std::cout << "foo(int*)" << std::endl;
}
晌融 2025-01-04 16:37:58

对于函数模板特化,您可以显式列出模板参数,但如果模板参数是推导得出的,则不必这样做。如果您不指定模板参数,编译器将使用与重载解析相同的规则来推导它们。为了决定选择哪个函数重载,编译器首先仅查看主模板(首先由一些神奇的过程选择)。查看两个可用的主模板,

template <typename T> void foo(T);
template <typename T> void foo(T*);

后者更适合指针参数。一旦找到正确的主模板,编译器就会查找该主模板的潜在特化。但是,您的示例#2 实际上并不是采用指针参数的函数模板的特化,尽管它涉及指针参数。如果您采用主声明

template <typename T> void foo(T*);

并将 T 替换为显式指定的模板参数 int*,您会得到

template <> void foo<int*>(int**);

也就是说,声明

template <> void foo<int*>(int*);

是不同的。您可能只想在指定模板参数时丢失指针:

template <> void foo<int>(int*);

For function template specializations you can explicitly list the template arguments but you don't have to if the template arguments are deduced. If you don't specify the template arguments, they are deduced by the compiler using the same rules as overload resolution. For deciding which function overload is to be chosen, the compiler starts with looking only at the primary templates (which are selected by some magical process in the first place). Looking at the two available primary templates

template <typename T> void foo(T);
template <typename T> void foo(T*);

the latter is a better match for a pointer argument. Once the proper primary template is found, the compiler looks for potential specializations of this primary template. However, your example #2 actually is not a specialization of function template taking a pointer argument although it involves a pointer argument. If you take the primary declaration

template <typename T> void foo(T*);

and you replace T by the explicitly specified template argument int* you get

template <> void foo<int*>(int**);

That is, the declaration

template <> void foo<int*>(int*);

is something different. You probably just want to lose the pointer when specifying the template argument:

template <> void foo<int>(int*);
半透明的墙 2025-01-04 16:37:58

我无法真正判断哪个函数 #2 应该专门化,或者确切地说极其复杂的重载解析规则将如何选择要调用的函数。

我确实知道您通常不需要专门化函数,而是可以依靠重载。要获得 int* 的函数,您只需要

void foo(int*) {
    std::cout << "foo(int*)" << std::endl;
}

只要参数匹配,非模板函数就会优先于模板。

I can't really tell which function #2 is supposed to specialize, or exactly how the extremely complicated overload resolution rules would select the function to call.

I do know that you most often don't need to specialize functions, but can rely on overloading instead. To get a function for int* you just need

void foo(int*) {
    std::cout << "foo(int*)" << std::endl;
}

A non-template function will be preferred over templates, as long as the parameter matches.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文