我们设这个:
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
定义中,并依赖于用户提供的专门化中不存在的私有成员别名。但是,用户可以通过提供适当的定义来创建显式专门化。所以这不是万无一失的。
ITER_CONCEPT(I)
,根据std::iterator_traits<I>
是来自主模板还是用户提供的专门化,它有不同的内部行为。当然,作为标准库,它们可以创建一个以__
为前缀的标识符作为主模板的成员,从而将任何来自用户空间的伪造企图声明为未定义行为。
这是一件只有编译器/标准库实现才能在万无一失中做到的事情,还是有可能在c++ 20中做到这一点?
最明显的解决方案是将私有成员别名或其他一些概念可检测的私有声明塞到主my_template定义中,并依赖于用户提供的专门化中不存在的私有声明。但是,用户可以通过提供适当的定义来创建显式专门化。所以这不是万无一失的。
基本上就是这样。例如,libstdc++的迭代器trait让它的主类模板继承自一个隐藏的基类模板,然后检查是否继承自该基类。
是的,用户可以通过提供适当的定义来创建显式专门化——但是,不要这样做。这不是你偶然会做的事情,这是明确而毫无意义的恶意,典型的说法是库和语言反对墨菲,而不是马基雅维利。
使用Modules,你可以通过导出主模板而不实际导出用来检查类模板是否专门化的基类,使用户更难以显式恶意:
export module std;
namespace std {
template <typename T> struct __iterator_traits { };
template <typename T>
export struct iterator_traits : __iterator_traits { };
}
现在用户甚至不能命名std::__iterator_traits
,以破坏库。也许反射仍然会给他们提供这样做的方法(假设这些东西仍然是可访问的),但现在他们真的要跳过很多圈。
说到反射,当前指定的API (P1240)的一部分包括is_specialization
,is_partial_specialization
和is_explicit_specialization
函数。如果它们的意思和我想的一样,那么当我们最终得到反射时,标准库就不必使用这些魔法基/魔法成员hack,而可以直接检查所提供的专门化是否是部分专门化或显式专门化。我认为库应该添加is_primary_specialization
以保持完整性。