为什么不能在完全专业化中引入新的模板参数?
Why can't new template parameters be introduced in full specializations?
/* 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++不支持此功能 - 我的一个要点提供了答案是否正确?
,这以有趣的方式爆炸。
为了清楚起见,我假设你的意思是你的示例代码,
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 标准。
沿着这些思路,您可能想出更多。以这种方式模糊类和类模板之间的界限势必会破坏各种东西。
> C++ 中的名称表示一种实体,这种实体通过名字查找来识别。
示例中A
的名称是类模板名称。因此,任何T
的A<T>
都是类名。
对于语言来说,这是一个有用的属性。破坏此属性意味着语言的复杂性大幅增加(对于编译器和用户(。
因此,即使你能找到所有的边缘情况并修复Wintermute指出的问题,它仍然很可能被拒绝,因为它使语言更加不规则,更令人惊讶,不那么统一。C++已经背负着相当多的不规则行为;添加更多是没有意义的。
我鼓励您发布您的预期用例,我们可能会向您展示更好的方法。
模板分辨率始终针对主模板。
在专业化中引入的任何其他模板参数都将是凭空悬挂的,没有任何内容可以将它们与任何模板使用联系起来。
C++ 中模板的核心语义之一是模板 id 具有单个模板参数列表:A<int>
。您提议的更改允许模板 ID 具有多个模板参数列表:A<int><double>
。
您可以使用模板默认参数在标准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
我不认为这种变化会破坏任何东西。下一个问题是它是否会改善任何东西?
- C++ 中的线程不能使用参数
- 为什么我们不能重复使用具有不同模板参数的别名模板标识符?
- 为什么我不能将引用作为 std::async 的函数参数传递
- Clang-Format 不能正确分配函数参数
- 为什么带有指针子对象的文字类类型的 constexpr 表达式不能是非类型模板参数
- 可变参数函数模板不能很好地使用 std::function 作为参数
- 为什么我不能用两个参数重载 C++ 运算符 []?
- 为什么std::{container}::template不能推导其参数类型
- 为什么在此上下文中非类型模板参数不能是自动的
- 为什么模板参数不能从 std::addressof 解析<int>?
- 为什么默认参数不能依赖于非默认参数?
- 为什么这个 const 参数不能与非类型模板参数匹配?
- 为什么 void 参数不能存在于具有多个参数的函数中?
- 为什么模板类型参数不能隐式强制转换?
- 为什么Qt信号的参数不能用typedef类型定义?
- Int函数参数不能正常工作
- v_list(可变数参数)不能正确转换
- omnet++:未知参数.不能在模拟中使用模块参数
- 在类声明中,为什么构造函数中的参数不能与私有部分中定义的变量同名?
- MSB3305:正在处理的COM引用-至少有一个参数不能被运行时封送处理程序封送