C++ 函数模板偏特化?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/8061456/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
C++ function template partial specialization?
提问by Narek
I know that the below code is a partial specialization of a class:
我知道下面的代码是一个类的部分特化:
template <typename T1, typename T2>
class MyClass {
…
};
// partial specialization: both template parameters have same type
template <typename T>
class MyClass<T,T> {
…
};
Also I know that C++ does not allow function template partial specialization (only full is allowed). But does my code mean that I have partially specialized my function template for one/same type arguments? Because it works for Microsoft Visual Studio 2010 Express! If no, then could you please explain the partial specialization concept?
我也知道 C++ 不允许函数模板部分特化(只允许完整)。但是我的代码是否意味着我已经部分地为一个/相同类型的参数专门化了我的函数模板?因为它适用于 Microsoft Visual Studio 2010 Express!如果不是,那么您能否解释一下部分专业化的概念?
#include <iostream>
using std::cin;
using std::cout;
using std::endl;
template <typename T1, typename T2>
inline T1 max (T1 const& a, T2 const& b)
{
return a < b ? b : a;
}
template <typename T>
inline T const& max (T const& a, T const& b)
{
return 10;
}
int main ()
{
cout << max(4,4.2) << endl;
cout << max(5,5) << endl;
int z;
cin>>z;
}
采纳答案by iammilind
In the example, you are actually overloading (not specializing)the max<T1,T2>
function. Partial specialization syntaxshould have looked somewhatlike below (had it been allowed):
在示例中,您实际上是在重载(不是专门化)该max<T1,T2>
函数。部分特化语法应该看起来有点像下面(如果它被允许):
//Partial specialization is not allowed by the spec, though!
template <typename T>
inline T const& max<T,T> (T const& a, T const& b)
{ ^^^^^ <--- specializing here
return 10;
}
[Note: in the case of a function template, only fullspecializationis allowed by the C++ standard (excluding the compiler extensions).]
[注:在一个功能模板的情况下,只有全专业化由C ++标准(不包括编译器扩展)允许的。]
回答by Rubens
Since partial specialization is not allowed -- as other answers pointed --, you could work around it using std::is_same
and std::enable_if
, as below:
由于不允许部分专业化 - 正如其他答案所指出的那样 - 您可以使用std::is_same
and解决它std::enable_if
,如下所示:
template <typename T, class F>
inline typename std::enable_if<std::is_same<T, int>::value, void>::type
typed_foo(const F& f) {
std::cout << ">>> messing with ints! " << f << std::endl;
}
template <typename T, class F>
inline typename std::enable_if<std::is_same<T, float>::value, void>::type
typed_foo(const F& f) {
std::cout << ">>> messing with floats! " << f << std::endl;
}
int main(int argc, char *argv[]) {
typed_foo<int>("works");
typed_foo<float>(2);
}
Output:
输出:
$ ./a.out
>>> messing with ints! works
>>> messing with floats! 2
Edit: In case you need to be able to treat all the other cases left, you could add a definition which states that already treated cases should not match-- otherwise you'd fall into ambiguous definitions. The definition could be:
编辑:如果您需要能够处理剩下的所有其他案例,您可以添加一个定义,指出已经处理过的案例不应匹配——否则您会陷入模棱两可的定义。定义可以是:
template <typename T, class F>
inline typename std::enable_if<(not std::is_same<T, int>::value)
and (not std::is_same<T, float>::value), void>::type
typed_foo(const F& f) {
std::cout << ">>> messing with unknown stuff! " << f << std::endl;
}
int main(int argc, char *argv[]) {
typed_foo<int>("works");
typed_foo<float>(2);
typed_foo<std::string>("either");
}
Which produces:
其中产生:
$ ./a.out
>>> messing with ints! works
>>> messing with floats! 2
>>> messing with unknown stuff! either
Although this all-casesthing looks a bit boring, since you have to tell the compiler everything you've already done, it's quite doable to treat up to 5 or a few more specializations.
虽然这种所有情况下的事情看起来有点无聊,因为你必须告诉编译器你已经完成的所有事情,所以处理多达 5 个或更多的专业化是完全可行的。
回答by Matthieu M.
What is specialization ?
什么是专业化?
If you really want to understand templates, you should take a look at functional languages. The world of templates in C++ is a purely functional sublanguage of its own.
如果你真的想了解模板,你应该看看函数式语言。C++ 中的模板世界本身就是一种纯函数式子语言。
In functional languages, selections are done using Pattern Matching:
在函数式语言中,选择是使用模式匹配完成的:
-- An instance of Maybe is either nothing (None) or something (Just a)
-- where a is any type
data Maybe a = None | Just a
-- declare function isJust, which takes a Maybe
-- and checks whether it's None or Just
isJust :: Maybe a -> Bool
-- definition: two cases (_ is a wildcard)
isJust None = False
isJust Just _ = True
As you can see, we overloadthe definition of isJust
.
如您所见,我们重载了 的定义isJust
。
Well, C++ class templates work exactly the same way. You provide a maindeclaration, that states the number and nature of the parameters. It can be just a declaration, or also acts as a definition (your choice), and then you can (if you so wish) provide specializations of the pattern and associate to them a different (otherwise it would be silly) version of the class.
好吧,C++ 类模板的工作方式完全相同。您提供一个主声明,说明参数的数量和性质。它可以只是一个声明,也可以作为一个定义(您的选择),然后您可以(如果您愿意)提供模式的特化并将它们关联到类的不同(否则会很愚蠢)版本.
For template functions, specialization is somewhat more awkward: it conflicts somewhat with overload resolution. As such, it has been decided that a specialization would relate to a non-specialized version, and specializations would not be considered during overload resolution. Therefore, the algorithm for selecting the right function becomes:
对于模板函数,特化有点尴尬:它与重载决议有些冲突。因此,已经决定特化将与非特化版本相关,并且在重载决议期间不会考虑特化。因此,选择正确函数的算法变为:
- Perform overload resolution, among regular functions and non-specialized templates
- If a non-specialized template is selected, check if a specialization exist for it that would be a better match
- 在常规函数和非专用模板之间执行重载决议
- 如果选择了非专业模板,请检查是否存在更匹配的专业化模板
(for on in-depth treatment, see GotW #49)
(有关深入处理,请参阅GotW #49)
As such, template specialization of functions is a second-zone citizen (literally). As far as I am concerned, we would be better off without them: I have yet to encounter a case where a template specialization use could not be solved with overloading instead.
因此,函数的模板特化是第二区公民(字面意思)。就我而言,没有它们我们会更好:我还没有遇到过模板特化使用无法通过重载来解决的情况。
Is this a template specialization ?
这是模板专业化吗?
No, it is simply an overload, and this is fine. In fact, overloads usually work as we expect them to, while specializations can be surprising (remember the GotW article I linked).
不,这只是一个过载,这很好。事实上,重载通常按我们的预期工作,而专业化可能令人惊讶(请记住我链接的 GotW 文章)。
回答by user2709407
Non-class, non-variable partial specialization is not allowed, but as said:
不允许非类、非变量的部分特化,但正如所说:
All problems in computer science can be solved by another level of indirection. —— David Wheeler
计算机科学中的所有问题都可以通过另一个间接层次来解决。——大卫·惠勒
Adding a class to forward the function call can solve this, here is an example:
添加一个类来转发函数调用可以解决这个问题,下面是一个例子:
template <class Tag, class R, class... Ts>
struct enable_fun_partial_spec;
struct fun_tag {};
template <class R, class... Ts>
constexpr R fun(Ts&&... ts) {
return enable_fun_partial_spec<fun_tag, R, Ts...>::call(
std::forward<Ts>(ts)...);
}
template <class R, class... Ts>
struct enable_fun_partial_spec<fun_tag, R, Ts...> {
constexpr static R call(Ts&&... ts) { return {0}; }
};
template <class R, class T>
struct enable_fun_partial_spec<fun_tag, R, T, T> {
constexpr static R call(T, T) { return {1}; }
};
template <class R>
struct enable_fun_partial_spec<fun_tag, R, int, int> {
constexpr static R call(int, int) { return {2}; }
};
template <class R>
struct enable_fun_partial_spec<fun_tag, R, int, char> {
constexpr static R call(int, char) { return {3}; }
};
template <class R, class T2>
struct enable_fun_partial_spec<fun_tag, R, char, T2> {
constexpr static R call(char, T2) { return {4}; }
};
static_assert(std::is_same_v<decltype(fun<int>(1, 1)), int>, "");
static_assert(fun<int>(1, 1) == 2, "");
static_assert(std::is_same_v<decltype(fun<char>(1, 1)), char>, "");
static_assert(fun<char>(1, 1) == 2, "");
static_assert(std::is_same_v<decltype(fun<long>(1L, 1L)), long>, "");
static_assert(fun<long>(1L, 1L) == 1, "");
static_assert(std::is_same_v<decltype(fun<double>(1L, 1L)), double>, "");
static_assert(fun<double>(1L, 1L) == 1, "");
static_assert(std::is_same_v<decltype(fun<int>(1u, 1)), int>, "");
static_assert(fun<int>(1u, 1) == 0, "");
static_assert(std::is_same_v<decltype(fun<char>(1, 'c')), char>, "");
static_assert(fun<char>(1, 'c') == 3, "");
static_assert(std::is_same_v<decltype(fun<unsigned>('c', 1)), unsigned>, "");
static_assert(fun<unsigned>('c', 1) == 4, "");
static_assert(std::is_same_v<decltype(fun<unsigned>(10.0, 1)), unsigned>, "");
static_assert(fun<unsigned>(10.0, 1) == 0, "");
static_assert(
std::is_same_v<decltype(fun<double>(1, 2, 3, 'a', "bbb")), double>, "");
static_assert(fun<double>(1, 2, 3, 'a', "bbb") == 0, "");
static_assert(std::is_same_v<decltype(fun<unsigned>()), unsigned>, "");
static_assert(fun<unsigned>() == 0, "");
回答by Puppy
No. For example, you can legally specialize std::swap
, but you cannot legally define your own overload. That means that you cannot make std::swap
work for your own custom class template.
不可以。例如,您可以合法地专业化std::swap
,但您不能合法地定义自己的过载。这意味着您无法std::swap
为自己的自定义类模板工作。
Overloading and partial specialization can have the same effect in some cases, but far from all.
在某些情况下,重载和部分特化可以产生相同的效果,但远非全部。
回答by Aconcagua
Late answer, but some late readers might find it useful: Sometimes, a helper function – designed such that it can be specialised – can solve the issue, too.
迟到的答案,但一些迟到的读者可能会发现它很有用:有时,一个辅助函数——设计成可以专门化——也可以解决这个问题。
So let's imagine, this is what we triedto solve:
所以让我们想象一下,这就是我们试图解决的问题:
template <typename R, typename X, typename Y>
void function(X x, Y y)
{
R* r = new R(x);
f(r, y); // another template function?
}
// for some reason, we NEED the specialization:
template <typename R, typename Y>
void function<R, int, Y>(int x, Y y)
{
// unfortunately, Wrapper has no constructor accepting int:
Wrapper* w = new Wrapper();
w->setValue(x);
f(w, y);
}
OK, partial template function specialisation, we cannot do that... So let's "export" the part needed for specialisation into a helper function, specialize that one and use it:
好的,部分模板函数特化,我们不能这样做......所以让我们将特化所需的部分“导出”到一个辅助函数中,特化那个并使用它:
template <typename R, typename T>
R* create(T t)
{
return new R(t);
}
template <>
Wrapper* create<Wrapper, int>(int n) // fully specialized now -> legal...
{
Wrapper* w = new Wrapper();
w->setValue(n);
return w;
}
template <typename R, typename X, typename Y>
void function(X x, Y y)
{
R* r = create<R>(x);
f(r, y); // another template function?
}
This canbe interesting especially if the alternatives (normal overloads instead of specialisations, the workaroundproposed by Rubens, ... – not that these are bad or mine is better, just anotherone) would share quite a lot of common code.
这可能很有趣,特别是如果替代方案(正常重载而不是专业化,鲁本斯提出的解决方法,...... - 不是说这些不好或我的更好,只是另一个)将共享相当多的公共代码。