C++ 为什么专门化一种类型的特质会导致未定义的行为? 讨论
根据标准§20.10.2/1标题C++ 为什么专门化一种类型的特质会导致未定义的行为? 讨论,c++,c++11,language-lawyer,undefined-behavior,c++14,C++,C++11,Language Lawyer,Undefined Behavior,C++14,根据标准§20.10.2/1标题概要[meta.type.synop]: 1除非另有规定,否则为本子条款中定义的任何类模板添加专门化的程序行为是未定义的 该特定条款与STL应可扩展的一般概念相矛盾,并阻止我们扩展类型特征,如下例所示: namespace std { template< class T > struct is_floating_point<std::complex<T>> : std::integral_constant &l
概要[meta.type.synop]:
1
除非另有规定,否则为本子条款中定义的任何类模板添加专门化的程序行为是未定义的
该特定条款与STL应可扩展的一般概念相矛盾,并阻止我们扩展类型特征,如下例所示:
namespace std {
template< class T >
struct is_floating_point<std::complex<T>> : std::integral_constant
<
bool,
std::is_same<float, typename std::remove_cv<T>::type>::value ||
std::is_same<double, typename std::remove_cv<T>::type>::value ||
std::is_same<long double, typename std::remove_cv<T>::type>::value
> {};
}
名称空间std{
模板
结构是浮点:std::integral\u常量
<
布尔,
std::值是否相同||
std::值是否相同||
std::值是否相同
> {};
}
其中,std::is_floating_point
被扩展为处理具有基础浮点类型的复数
问题
是什么原因使得标准化委员会决定类型特征不应该被专门化
未来是否有计划撤销这一限制
对于为浮点数的主要类型类别,有一个设计不变量:
对于任何给定类型T
,只有一个主要类型类别
计算结果为true
的值成员
参考:(20.10.4.1主要类型类别[meta.one.cat])
程序员在检查一些未知的泛型类型时,可以依赖泛型代码中的这个不变量T
:也就是说,如果是class::value
是true
,那么我们就不需要检查是浮点::value
。我们保证后者为false
这是一张图表
表示主要和复合类型性状(此图顶部的叶子为主要类别)
如果允许(例如)std::complex
对is_class
和is_floating_point
的回答都为真,那么这个有用的不变量将被破坏。程序员将不再能够依赖这样一个事实:如果是浮点::value==true
,那么T
必须是float
、double
或long double
中的一个
现在有一些特性,标准确实“不这样说”,并且允许对用户定义的类型进行专门化<代码>普通类型
就是这样一种特征
对于初级和复合型性状,没有计划放松对这些性状进行专门化的限制。这样做会损害这些特征对C++中生成的每一种类型的精确和唯一分类的能力。< p> >添加到霍华德的答案(以实例)。
如果用户被允许专门化类型特征,他们可能会撒谎(故意或错误),标准库无法再确保其行为是正确的
例如,当复制类型为std::vector
的对象时,流行实现所做的优化就是调用std::memcpy
来复制所有元素,前提是T
可以轻松地复制构造。他们可能使用std::is_minality_copy_constructible
来检测优化是否安全。如果没有,那么实现将返回到安全但较慢的方法,该方法将在元素之间循环并调用T
的复制构造函数
现在,如果一个人专门为T=std::shared\u ptr
做std::is_琐碎地\u copy\u可构造的,如下所示:
namespace std {
template <>
class is_trivially_copy_constructible<std::shared_ptr<my_type>> : std::true_type {
};
}
名称空间std{
模板
类是可构造的:std::true\u类型{
};
}
那么复制std::vector将是灾难性的
这不是标准库实现的错,而是专门化编写者的错。从某种程度上说,OP提供的引语就是这样说的:“这是你的错,不是我的。”@billz怎么办?这个程序展示了UB。为什么不struct is_floating_point:public std::is_floating::point{}代码>?@ MUN3362626:是比较好的,但也是不相关的,因为您的版本也显示了未定义的行为。即使允许,专攻“代码> STD::IsOffoLoint Point < /代码>,并使整个翻译单元考虑<代码> STD::复杂< /代码>是一个浮点类型,这样您就可以检查一下。“是std::complex
还是浮点“在你的活动中,有一项活动就像是炸毁你的房子,让里面有新鲜空气。类型特征类查询类型的基本属性,是模板元程序的基本构建块。允许你为它们添加专门化是没有意义的。还有一种“不变量”,主要类型类别特征也反映了事实。也就是说,诸如“浮点类型”之类的类别在标准中有具体的描述,它们非常不可扩展或用户自定义。因此,用户定义的专门化要么重新声明明显的,要么撒谎。@重复数据消除:好的,我已经删除了嵌入代码>。如何修改标准中的错误?比如把std::complex
简单化为默认的可构造的
(正如我们所知道的那样)。这个例子非常具有指导性,很好地解决了这个问题。我认为这个例子并没有很好地说明这个问题。这个例子产生了中断的程序,不是因为我们专门化了一个不应该专门化的类型,而是因为我们提供了关于类型的错误信息(共享指针)。如果我们从false_类型继承,那么程序的行为同样是未定义的,而实际上它可以很好地工作,因为专门化将提供正确的信息。作为一个补充说明,为了保证正确的向量复制,库还必须正确地实现copyctor,但实现它并不违法。