C++ 为什么专门化一种类型的特质会导致未定义的行为? 讨论

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

根据标准§20.10.2/1标题
概要[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,但实现它并不违法。