似乎很难将C概念组合成一种可以用作对一些新模板的约束的方式,因为概念模板没有完全被视为类模板(只是一种)。
实际上,我想做的工作在Visual Studio编译器上,但不是clang/g。
Compiler Explorer Illustration
背景
template <typename ITERABLE, typename OF_T>
concept IIterable = ranges::range<ITERABLE> and is_convertible_v<ranges::range_value_t<ITERABLE>, OF_T>;
static_assert (IIterable<vector<int>, int>);
static_assert (IIterable<vector<long int>, int>);
static_assert (IIterable<vector<int>, long int>);
static_assert (not IIterable<vector<string>, int>); // legit 'iterable' but cannot convert string to int
然后用作对一些其他模板的约束(例如,容器的构造函数参数)。
template <typename T>
struct Collection { ...
template <IIterable<T> ITERABLE_OF_ADDABLE>
Collection (ITERABLE_OF_ADDABLE&& src)
这在我测试过的所有编译器上都运行得很好。但是现在我希望泛化Iterable的概念,所以我对输入迭代对象的元素有一个通用的约束
// 'range + some other constraint'
template <typename ITERABLE, template <typename> typename ITEM_PREDICATE>
concept IIterableWith = ranges::range<ITERABLE> and ITEM_PREDICATE<ranges::range_value_t<ITERABLE>>::value;
到目前为止一切顺利。现在它得到了一个小把戏。如何定义元素上的“constaint”。我想使用原生C++概念'currying'概念,并且能够传入convertible_to
template <typename ITERABLE, typename OF_T>
concept IIterable1 = IIterableWith<ITERABLE, convertible_to<OF_T>>;
但是模板类型currying似乎不适用于这种上下文中的任何编译器。没问题我可以自己写。
template <typename T>
struct IsAddableOfT {
// extra confusing level of struct/tests due to fact that type currying only works on concepts
// not class templates, and cannot just write two 'template' statements in a row for an alias template - must break up like this.
template <typename POTENTIALLY_ADDABLE_T>
using Test = is_convertible<POTENTIALLY_ADDABLE_T, T>;
};
这在我选择的任何编译器上都工作得很好(到目前为止)。
现在的问题!
template <typename ITERABLE, typename OF_T>
concept IIterable = IIterableWith<ITERABLE, typename IsAddableOfT<OF_T>::Test>;
这在visual studio编译器上工作正常(参见上面的编译器资源管理器链接)-但在gcc和clang上失败(注意,使用visual studio需要在IsAddableOfT之前输入typename,并尝试使用模板和不使用clang/gcc:仍然没有运气)。
type/value mismatch at argument 2 in template parameter list for 'template<class ITERABLE, template<class> class ITEM_PREDICATE> concept IIterableWith
注意-一个很好的解决方案是如果存在一个CONCEPT(不是类型trait),比如:
template <concept <typename>A, concept <typename>B>
concept conjunction = A && B
2条答案
按热度按时间txu3uszq1#
你必须使用
template
消歧器,而不是typename
,因为IsAddableOfT::Test
不是类型,而是模板。以下代码与GCC 13-std=c++20
编译:template
消歧义器自C23以来已被弃用,但在C20* 中是必要的。ejk8hzay2#
visual studio是错误的,而且总是错误的代码类型。
看看
IIterableWith
:正如您所看到的,第二个参数是 * template *,而不是类型名。
现在让我们玩个游戏。你认为这应该编译吗?
现在我打赌你会回答我没有,因为
int
本身不是模板。好的,还有一步:这个怎么样?
如果你仍然回答我,它必须不编译,你是对的!
OF_T
不是模板。但是编译器怎么知道呢?编译器甚至不知道OF_T
实际上是什么,它仍然是一个需要填充的模板参数。这是因为编译器仍然必须在模板名称查找的第一阶段构造一个部分AST。每个模板参数都有 * kind *。
OF_T
是typename
,所以我们知道它不能是模板或值。它必须是一种类型。这是好事!这意味着编译器可以在示例化模板之前验证代码是否有意义。现在让我们更进一步,再添加一层。这个编译吗?
当然不是!你必须告诉
value_type
是正确的吗?value_type
必须是一个类型,对吗?这是因为编译器假定依赖名称是值而不是类型。假设::value_type
是这个表达式的一个类型。让我们修复它:好了但它仍然不会编译。
但是等一下!为什么我们必须这样做,为什么
value_type
被假设为值而不是类型?这是因为编译器不知道
std::vector<OF_T>
的内容是什么。根据OF_T
是什么,std::vector
可以接受任何专业化。您可能会想象有人用static int value_type;
定义示例化。编译器不能假设任何内容,因为它是一个 * 依赖类型 *。依赖于模板的类型。依赖类型和模板参数一样未知!所以在依赖表达式上,就像这样:
typename std::vector<OF_T>::value_type
,value_type
是实际类型的唯一原因是因为typename
。现在一切都好了,让我们试试你的代码:
所以这里你说
::Test
必须是一个类型,就像int一样。在编译器看来,
Test
必须和int
或OF_T
一样是一个类型,因为你明确地说你期望::Test
是一个类型。你必须告诉编译器
Test
是一个模板,而不是说它是一个类型:现在起作用了!
IIterableWith
需要一个模板,在这个表达式中,编译器预先知道Test
也必须是一个模板。匹配就能编译。