在Where in the C++11 standard does it prohibit 'template <typename T> class A {...}; template <typename T> class A<int> {...};' (if anywhere)?中,已确认C ++ 11标准中不允许使用以下语法:
/* invalid C++ */
template <typename T>
class A
{
public:
T t;
};
// phony "full specialization" that mistakenly attempts
// to introduce a *new* template parameter
template <typename T>
class A<int>
{
public:
int i;
T t;
};
完全理解上述语法并不代表有效的C ++,但我可以想象一下语法上明确使用上面的代码片段,如下所示:
A<float> a1;
A<int><double> a2;
a1.t = 2.0f;
a2.i = 2;
a2.t = 2.0;
对于C ++来说,支持上面的语法似乎在语法和语义上都是明确的。
(如果任何人都不清楚预期的语义,请发表评论,我会解释。)
我会将此语法描述为“在完全专业化中引入新模板参数”。
在这个被修改为支持上述语法和语义的C ++编译器的想象场景中,编译器会看到A<int> a2;
并认识到尝试的实例化与主模板匹配;然后它将搜索特化并查找并选择完整的专业化A<int>
(忽略<double>
);然后会注意到这个完整的专业化引入了一个新的模板参数T
,在声明的变量a2
的情况下,它是double
。
如果我说上面的语法和语义是明确的,那么我想知道为什么这不是C ++的一部分。我可以想到三个原因,但也许答案是不同的或更复杂的。
我想知道为什么C ++不支持这一点 - 我是否认为我的一个要点提供了答案?
答案 0 :(得分:5)
为清楚起见,我假设您的意思是使用您的示例代码,
A<double> a; // okay
A<int> a2; // not okay, A<int> is not a class but a class template.
现在让我们尝试在其他模板中使用它。考虑
template<typename T>
void function<A<T> const &a) { ... }
A<double> a;
A<int><double> a2;
function(a); // that looks okay.
function(a2); // er...compiler error, I guess?
那还不错;编译器可能会抱怨这一点。我想我们已经开始看到这有点奇怪了。好吧,把它提升一个档次:
template<template<typename> class A> struct Foo { ... };
Foo<A> f; // this would usually work, does it now?
如果你回答否,如果在编译时不知道专业化怎么办?
编辑:稍微扩展一下,考虑模板模板参数的真实场景,我喜欢称之为包特征:
template<template<typename> class, typename...> struct applies_to_all;
template<template<typename> class predicate, typename T, typename... Pack>
struct applies_to_all<predicate, T, Pack...> {
static bool const value =
predicate<T>::value && applies_to_all<predicate, Pack...>::value;
};
template<template<typename> class predicate>
struct applies_to_all<predicate> {
static bool const value = true;
};
...
bool b = applies_to_all<std::is_integral, int, long, double>::value;
并提供A
之类的内容,除了int
之外的所有内容以及int
的类模板。然后尝试解决
applies_to_all<A, double, float, std::string, int>::value
请注意,这个简单的案例不是您可能会看到的代码。你真正得到的是这些东西嵌套在其他模板中的三个层次,它看起来像这样:
applies_to_all<A, T...>::value
可能会为此定义一个在数学上一致的行为,但我怀疑可以定义一个有用的行为。当然,它不符合POLA标准。
你可能会在这些方面想出更多的东西。以这种方式模糊类和类模板之间的界限必将打破各种各样的东西。
答案 1 :(得分:3)
C ++中的名称代表一种实体,这种名称通过名字查找而闻名。
示例中的名称A
是类模板名称。因此,任何A<T>
的{{1}}都是类名。
这是语言的有用属性。打破这个属性意味着语言在复杂性方面会有很大的提升(对于编译器和用户来说都是如此)。
因此,即使你能找到所有边缘案例并解决Wintermute指出的问题,它仍然很可能被拒绝,因为它使语言更不规则,更令人惊讶,更不统一。 C ++已经受到相当多的不规则性的负担;添加更多内容是没有意义的。
我建议您发布预期的用例,我们可能会向您展示更好的方法。
答案 2 :(得分:1)
模板分辨率始终针对主模板。
专业化中引入的任何其他模板参数都只是悬而未决,没有任何内容可以将它们连接到任何模板使用。
答案 3 :(得分:1)
C ++中模板的核心语义之一是template-id有一个模板参数列表:A<int>
。您提议的更改允许模板ID具有多个模板参数列表:A<int><double>
。
您可以使用模板default-arguments在标准C ++中实现所需的结果:
template <typename T, typename U = void>
struct A
{
T t;
};
template <typename U>
struct A<int, U>
{
int i;
T t;
};
A<float> a1;
A<int, double> a2;
a1.t = 2.0f;
a2.i = 2;
a2.t = 2.0;
语法A<int, double>
比A<int><double>
更简单 - 一个参数列表而不是多个参数列表 - 这是我不能进行建议更改的主要原因。
话虽如此,有可能放宽模板的所有显式/部分特化都采用相同数量的模板参数的要求。相反,可能需要专门化采用与主模板一样多的模板参数。如果是这种情况,您可以省略主模板中的冗余第二个参数。
template <typename T>
struct A; // primary template, definition omitted for brevity
template <typename U>
struct A<int, U>; // partial specialization, definition omitted for brevity
A<float> a1; // OK, selects primary template
A<int, double> a2; // OK, selects partial specialization
A<float, float> a3; // Error, no valid specialization
我不认为这种改变会破坏任何事情。接下来的问题是它是否会改善什么?