Dr.*_*Gut 25 c++ template-specialization c++-concepts c++20
适用于类的语法不适用于概念:
template <class Type>
concept C = requires(Type t) {
// ...
};
template <class Type>
concept C<Type*> = requires(Type t) {
// ...
};
Run Code Online (Sandbox Code Playgroud)
MSVC 对于“专业化”行的说法是:error C7606: 'C': concept cannot be explicitly instantiated, explicitly specialized or partially specialized
。
为什么概念不能专门化?有理论上的原因吗?
Nic*_*las 29
因为它会破坏约束规范化和包含规则。
就目前情况而言,每一个concept
都有确切且只有一个定义。因此,概念之间的关系是已知且固定的。考虑以下:
template<typename T>
concept A = atomic_constraint_a<T>;
template<typename T>
concept B = atomic_constraint_a<T> && atomic_constraint_b<T>;
Run Code Online (Sandbox Code Playgroud)
根据 C++20 的当前规则,B
包含A
. 这是因为,在约束规范化之后,B
包括 的所有原子约束A
。
B
如果我们允许概念的专门化,那么和now之间的关系A
取决于提供给这些概念的参数。B<T>
可能包含A<T>
某些T
s 但不包含其他T
s。
但这不是我们使用概念的方式。如果我试图编写一个比另一个模板“更受约束”的模板,那么唯一的方法就是使用一组已知的、定义良好的概念。这些定义不能依赖于这些概念的参数。
编译器应该能够计算一个受约束的模板是否比另一个受约束的模板更受约束,而无需任何模板参数。这很重要,因为让一个模板比另一个模板“更受约束”是使用概念和约束的一个关键特征。
具有讽刺意味的是,允许概念专业化会破坏其他模板的(受限)专业化。或者至少,它会使其很难实施。
除了 Nicol Bolas 的精彩回答之外:
\n概念有点特殊,因为它们的行为与其他模板化的东西不同:
\n\n\n\n(5)概念未实例化 ( [temp.spec] )。
\n
\n[注1:concept-id ( [temp.names] ) 被评估为表达式。概念不能显式实例化 ( [temp.explicit] )、显式专用 ( [temp.expl.spec] ) 或部分专用 ( [temp.spec.partial] )。\xe2\x80\x94尾注]
由于概念无法实例化,因此它们也无法专门化。
\n我不确定为什么标准决定不让它们专业化,因为很容易模拟专业化。
\n虽然您无法直接专门化概念,但有很多方法可以解决该问题。
\n您可以在概念中使用任何类型的常量表达式 - 因此您可以使用模板化变量(可以专门化)并将其包装到一个概念中 - 该标准也对很多自己的概念这样做,例如std::is_intergral
:
template<class T> struct is_integral;\n\n// is_integral is specialized for integral types to have value == true\n// and all others are value == false\n\ntemplate<class T>\ninline constexpr bool is_integral_v = is_\xc2\xadintegral<T>::value;\n\ntemplate<class T>\nconcept integral = is_\xc2\xadintegral_\xc2\xadv<T>;\n\n
Run Code Online (Sandbox Code Playgroud)\n因此,您可以轻松地编写一个具有如下专业化的概念:godbolt 示例
\nstruct Foo{};\nstruct Bar{};\n\ntemplate<class T>\nconstexpr inline bool is_addable_v = requires(T t) {\n { t + t } -> std::same_as<T>;\n};\n\n// Specializations (could also use other requires clauses here)\ntemplate<>\nconstexpr inline bool is_addable_v<Foo> = true;\n\ntemplate<class T>\nconstexpr inline bool is_addable_v<T&&> = true;\n\ntemplate<class T>\nconcept is_addable = is_addable_v<T>; \n\nint main() {\n static_assert(is_addable<int>);\n static_assert(is_addable<Foo>);\n static_assert(!is_addable<Bar>);\n static_assert(is_addable<Bar&&>);\n}\n
Run Code Online (Sandbox Code Playgroud)\n或者通过使用一个类:
\ntemplate<class T>\nstruct is_addable_v : std::true_type {};\n\ntemplate<>\nstruct is_addable_v<struct FooBar> : std::false_type {};\n\ntemplate<class T>\nconcept is_addable = is_addable_v<T>::value; \n
Run Code Online (Sandbox Code Playgroud)\n或者甚至是 constexpr lambda: godbolt 示例
\n// pointers must add to int\n// everything else must add to double\ntemplate<class T>\nconcept is_special_addable = ([](){\n if constexpr(std::is_pointer_v<T>)\n return requires(std::remove_pointer_t<T> t) {\n { t + t } -> std::same_as<int>;\n };\n else\n return requires(T t) {\n { t + t } -> std::same_as<double>;\n };\n})(); \n\nint main() {\n static_assert(is_special_addable<double>);\n static_assert(is_special_addable<int*>);\n static_assert(!is_special_addable<double*>);\n static_assert(!is_special_addable<int>);\n}\n
Run Code Online (Sandbox Code Playgroud)\n因此,虽然概念本身无法专门化,但利用现有的语言功能很容易达到相同的效果。
\n对这种情况的专业化会带来一袋蠕虫。我们通过模板专业化打开了这个包一次。模板专业化是使通用图灵模板语言完整的主要部分。是的,您可以在模板中进行编程。你不应该,但你可以。Boost 有一个名为 Boost.MPL 的库,其中充满了巧妙的东西,例如在编译时而不是运行时运行的“无序映射”。
所以我们必须仔细限制它。简单的情况可能有效,但复杂的情况必须被禁止。当然,任何能够远程创建递归约束的东西都必须仔细观察。事实上,考虑一个概念:
template <typename T>
concept hailstone = false;
template <int i>
concept hailstone<std::integral_constant<int, i> =
hailstone<2 * i> || (i % 2 == 1 && hailstone<3*i - 1>);
template <>
concept hailstone<std::integral_constant<int, 0> = true;
Run Code Online (Sandbox Code Playgroud)
那么,是std::integral_constant<int, 27>
冰雹吗?这可能需要一段时间。我选择的例子是基于科拉茨猜想中的冰雹数字。确定任何给定的数字是否是冰雹是非常困难的(尽管据我们所知,每个数字都是冰雹数字)。
现在用一个可以实现任意精度的巧妙结构替换integral_constant。现在我们有麻烦了!
现在我们可以仔细地分割这个问题的元素并将它们标记为可行。规范社区不属于这个行业。我们在 C++20 中所知道的概念被昵称为concepts-lite,因为它实际上是概念库的一个大大简化的版本,而该概念库从未出现在 C++11 中。该库有效地实现了描述逻辑,这是一类已知可判定的逻辑。这很重要,因为计算机必须运行所有必要的计算,而我们不希望它们花费无限的时间。概念就是由此衍生出来的,所以它遵循相同的规则。而且,如果您查看描述逻辑,您证明许多陈述的方式涉及首先枚举所有命名概念的列表。一旦你列举了这一点,证明你可以在有限的时间内解决任何概念要求就很简单了。
正如尼科尔波拉斯在他的回答中指出的那样,概念的目的不是成为某种聪明的图灵完整系统。这是为了提供更好的错误消息。因此,虽然人们可能能够在精心选择的路径中巧妙地进入某些专业领域,但没有动力这样做。
归档时间: |
|
查看次数: |
3198 次 |
最近记录: |