C++ 概念精简版和类型别名声明
C++ concepts lite and type alias declaration
是否可以使用 typedef
或 using
在概念中声明类型别名,如 Concepts TS 所提议的那样?
如果我尝试类似下面的 MWE,代码不会编译(使用 gcc 6.2.1 和 -fconcepts
开关)
#include <type_traits>
template<typename T>
concept bool TestConcept ()
{
return requires(T t)
{
using V = T;
std::is_integral<V>::value;
};
}
int main()
{
return 0;
}
产生的错误:
main.cpp: In function ‘concept bool TestConcept()’:
main.cpp:8:9: error: expected primary-expression before ‘using’
using V = T;
^~~~~
main.cpp:8:9: error: expected ‘}’ before ‘using’
main.cpp:8:9: error: expected ‘;’ before ‘using’
main.cpp:4:14: error: definition of concept ‘concept bool TestConcept()’ has multiple statements
concept bool TestConcept ()
^~~~~~~~~~~
main.cpp: At global scope:
main.cpp:11:1: error: expected declaration before ‘}’ token
}
^
没有。根据概念TS,一个要求是:
requirement:
simple-requirement
type-requirement
compound-requirement
nested-requirement
其中 简单要求 是 表达式 后跟 ;
和 类型-要求 类似于 typename T::inner
。另外两个听起来就像名字所暗示的那样。
类型别名是一个声明,而不是一个表达式,因此不符合要求的要求。
This feels unnecessarily restrictive to me. Do you know if there exists a reasonable workaround instead of writing the same complicated type over and over again?
您可以将约束的实现推迟到另一个概念,将这些类型作为模板参数传递:
template<typename Cont, typename It, typename Value>
concept bool InsertableWith = requires(Cont cont, It it, Value value) {
// use It and Value as much as necessary
cont.insert(it, std::move(value));
};
template<typename Cont>
concept bool Insertable = requires {
// optional
typename Cont::const_iterator;
typename Cont::value_type;
} && InsertableWith<Cont, typename Cont::const_iterator, typename Cont::value_type>;
如果您正在考虑这样做,我建议您在做出决定之前尝试简单的例子。你如何编写你的概念和约束决定了编译器将如何报告错误,当然,有良好的错误是使概念有用的重要组成部分。让我的概念更容易写,同时让错误更难理解,这不是我会掉以轻心的权衡。
例如,这就是为什么我多余地添加 typename Cont::const_iterator;
作为显式约束的原因。这使编译器有机会报告此类型要求。我在选择 InsertableWith
作为概念的名称时也很谨慎:我本可以很容易地使用 detail::Insertable
,但是涉及 Insertable
和 detail::Insertable
的错误可能是结果更加混乱。
最后请注意,这一切都取决于编译器的实现质量,因此我暂时不期望任何方法是确定的。我鼓励玩这个 Coliru demo.
是否可以使用 typedef
或 using
在概念中声明类型别名,如 Concepts TS 所提议的那样?
如果我尝试类似下面的 MWE,代码不会编译(使用 gcc 6.2.1 和 -fconcepts
开关)
#include <type_traits>
template<typename T>
concept bool TestConcept ()
{
return requires(T t)
{
using V = T;
std::is_integral<V>::value;
};
}
int main()
{
return 0;
}
产生的错误:
main.cpp: In function ‘concept bool TestConcept()’:
main.cpp:8:9: error: expected primary-expression before ‘using’
using V = T;
^~~~~
main.cpp:8:9: error: expected ‘}’ before ‘using’
main.cpp:8:9: error: expected ‘;’ before ‘using’
main.cpp:4:14: error: definition of concept ‘concept bool TestConcept()’ has multiple statements
concept bool TestConcept ()
^~~~~~~~~~~
main.cpp: At global scope:
main.cpp:11:1: error: expected declaration before ‘}’ token
}
^
没有。根据概念TS,一个要求是:
requirement:
simple-requirement
type-requirement
compound-requirement
nested-requirement
其中 简单要求 是 表达式 后跟 ;
和 类型-要求 类似于 typename T::inner
。另外两个听起来就像名字所暗示的那样。
类型别名是一个声明,而不是一个表达式,因此不符合要求的要求。
This feels unnecessarily restrictive to me. Do you know if there exists a reasonable workaround instead of writing the same complicated type over and over again?
您可以将约束的实现推迟到另一个概念,将这些类型作为模板参数传递:
template<typename Cont, typename It, typename Value>
concept bool InsertableWith = requires(Cont cont, It it, Value value) {
// use It and Value as much as necessary
cont.insert(it, std::move(value));
};
template<typename Cont>
concept bool Insertable = requires {
// optional
typename Cont::const_iterator;
typename Cont::value_type;
} && InsertableWith<Cont, typename Cont::const_iterator, typename Cont::value_type>;
如果您正在考虑这样做,我建议您在做出决定之前尝试简单的例子。你如何编写你的概念和约束决定了编译器将如何报告错误,当然,有良好的错误是使概念有用的重要组成部分。让我的概念更容易写,同时让错误更难理解,这不是我会掉以轻心的权衡。
例如,这就是为什么我多余地添加 typename Cont::const_iterator;
作为显式约束的原因。这使编译器有机会报告此类型要求。我在选择 InsertableWith
作为概念的名称时也很谨慎:我本可以很容易地使用 detail::Insertable
,但是涉及 Insertable
和 detail::Insertable
的错误可能是结果更加混乱。
最后请注意,这一切都取决于编译器的实现质量,因此我暂时不期望任何方法是确定的。我鼓励玩这个 Coliru demo.