检测类型是来自主模板的专门化还是用户提供的专门化
假设我有这个:
template<typename T>
class my_template {...};
现在,用户可以针对自己的类型专门化my_template
。它们会将这些类型传递给我的一些API函数,这些API函数将使用my_template<T>
的属性进行操作。
所以在我的代码中的某个点上,我有一个my_template<T>
。我想要某种类型的元函数,如果my_template<T>
是用户提供的专门化(部分或显式),则接受my_template<T>
并生成编译时值true
,如果不是,则false
。
concept
可检测的私有声明添加到主my_template
定义中,并依赖于用户提供的专门化中不存在的成员别名。但是,用户可以通过提供适当的定义来伪造明确的专门化。所以这并不是万无一失的。
这个问题不是诡辩。C++20规范有一个元函数ITER_CONCEPT(I)
,根据std::iterator_traits<I>
是来自主模板还是来自用户提供的专门化,该元函数具有不同的内部行为。当然,作为标准库,它们可以创建一个前缀为__
的标识符作为主模板的成员,从而将来自用户空间的任何企图伪造声明为未定义的行为。
只有编译器/标准库实现才能万无一失地做到这一点,还是在C++20中可以做到这一点?
解决方案
最明显的解决方案是将私有成员别名或其他一些可检测到概念的私有声明添加到主要的MY_TEMPLATE定义中,并依赖于用户提供的专门化中没有的成员别名。但是,用户可以通过提供适当的定义来伪造明确的专门化。所以这并不是万无一失的。
基本上就是这样,是的。例如,libstdc++的迭代器特征使其主类模板继承自隐藏的基类模板,然后检查从该基类继承。
是的,用户可以通过提供适当的定义来打造显式的专门化--但就像这样做。这不是你会偶然做的事情,这显然是毫无意义的恶意行为,典型的说法是,库和语言抵御的是墨菲,而不是马基雅维利。
使用模块,您可以通过导出主模板但不实际导出用于检查类模板是否专门化的基类来使用户更难显式恶意:
export module std;
namespace std {
template <typename T> struct __iterator_traits { };
template <typename T>
export struct iterator_traits : __iterator_traits { };
}
现在,用户甚至无法命名std::__iterator_traits
来拆分库。也许反思仍然会给他们提供一种方法来做到这一点(假设这些东西仍然可以访问),但现在他们真的会经历很多困难。
说到反射,当前指定的接口(P1240)的一部分包括函数is_specialization
、is_partial_specialization
和is_explicit_specialization
。如果这些都是我认为它们的意思,那么当我们最终得到反射时,库就不必使用这些魔法库/魔术成员黑客,而只需直接检查所提供的专门化是部分专门化还是显式专门化。我认为为了完整性,库可能应该添加is_primary_specialization
。
相关文章