我想写一个概念 Indexable 意思是一个序列要么有开始/结束,返回 RandomAccessIterator,要么 operator[] 被定义并返回一个非 void 类型的值。
我将Stroustrup 文章中的想法用于 Sequence 概念,并对其进行了扩充:
template <class T>
concept bool Indexable = Sequence<T> || requires(T t, size_t n) {
{ t[n] } -> NotVoid;
};
Run Code Online (Sandbox Code Playgroud)
它适用于大多数情况,但在以下情况下失败:
struct Bad {
std::vector<int> nums;
private:
int& operator[](size_t ind) {
return nums[ind];
}
};
static_assert(!Indexable<Bad>, "fail");
Run Code Online (Sandbox Code Playgroud)
出于某种原因,我的概念忽略了 operator[] 被定义为私有并返回 true 的事实。我错过了什么?
最近意外发现gcc和msvc接受如下代码(注意template-list中的requires-clause):
#include <vector>
template <template <class> requires true class>
void f() {}
int main() {
f<std::vector>();
}
Run Code Online (Sandbox Code Playgroud)
和 clang拒绝它的语法:
<source>:3:28: error: template template parameter requires 'class' after the parameter list
template <template <class> requires true class>
^
Run Code Online (Sandbox Code Playgroud)
我应该信任哪个编译器?这段代码在语法上有效吗?
我已经知道这个概念是一个编译时谓词,它可以约束模板或auto.
我发现这些概念将返回 type 的纯右值bool,所以我可以打印它来测试自己。
我还阅读了这个特定问题Can概念(C++ 20)可以用作布尔值吗?以及澄清可用作布尔值的概念的答案。
[temp.names] (8) 一个concept-id 是一个simple-template-id,其中template-name 是一个concept-name。概念 ID 是 bool 类型的纯右值,并且不命名模板特化。如果指定的模板参数满足概念的规范化约束表达式 ([temp.constr.constr]),则概念 ID 评估为真,否则为假。
例如:
template <typename T>
concept meowable = requires (T obj) {
{ obj.meow() } -> std::same_as<T&>;
};
struct Dog {};
struct Cat { Cat& meow(); };
struct Wolf { Wolf& woof(); };
Run Code Online (Sandbox Code Playgroud)
应用:
std::cout << std::boolalpha;
std::cout << "Has meow?" << '\n'
<< "Dog: " << meowable<Dog> << '\n'
<< "Cat: " << meowable<Cat> << '\n' …Run Code Online (Sandbox Code Playgroud) c++ type-traits template-meta-programming c++-concepts c++20
这个问题的主要目的是提请社区注意 libstdc++ 范围不适用于 clang:https ://bugs.llvm.org/show_bug.cgi ? id = 46746
Avi Kivity 建议这是一个 gcc 错误:https ://gcc.gnu.org/bugzilla/show_bug.cgi?id=97120
但随后他也暗示这是一个叮当声错误:https ://bugs.llvm.org/show_bug.cgi ? id = 47509
Rafael Ávila de Espíndola 将问题归结为以下代码,该代码使用 gcc 编译,但不使用 clang:
template <typename _Tp>
concept __member_begin = requires(_Tp __t) {
{__t.begin()};
};
template <typename _Tp>
concept nothing = requires(_Tp __t) {
{42};
};
template <typename _Tp>
requires __member_begin<_Tp> void __ranges_begin() {}
template <typename _Derived>
struct view_interface {
void foo() requires __member_begin<_Derived> {}
void bar() requires nothing<decltype(__ranges_begin<_Derived>())> {}
};
struct …Run Code Online (Sandbox Code Playgroud) 在C++ 14中,无法使用多个参数包调用函数模板:
#include <future>
template<class... Futures, class... Incrementables>
void foo(Futures&... futures, Incrementables... incrementables)
{
}
int main()
{
std::future<int> a, b;
int x, y;
// ERROR
foo(a, b, x, y);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
因为不清楚第一个参数包的结束位置和第二个参数包的开始位置,所以foo如果没有程序员提供的附加信息,则无法调用.
但是,似乎两个参数包原则上可以消除歧义,给出适当的概念Future和Incrementable.
即将推出的C++ Concepts技术规范的任何功能是否会放宽这些限制并允许调用具有多个参数包的函数模板?
是否可以使用typedef或using在概念中声明类型别名,如概念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;
}
Run Code Online (Sandbox Code Playgroud)
结果错误:
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 ()
^~~~~~~~~~~ …Run Code Online (Sandbox Code Playgroud) 该cppreference.com指出:
概念不能递归引用自己
但是我们如何定义一个表示整数或整数向量或整数向量的概念,等等。
我可以有这样的东西:
template < typename Type > concept bool IInt0 = std::is_integral_v<Type>;
template < typename Type > concept bool IInt1 = IInt0<Type> || requires(Type tt) { {*std::begin(tt)} -> IInt0; };
template < typename Type > concept bool IInt2 = IInt1<Type> || requires(Type tt) { {*std::begin(tt)} -> IInt1; };
static_assert(IInt2<int>);
static_assert(IInt2<std::vector<int>>);
static_assert(IInt2<std::vector<std::vector<int>>>);
Run Code Online (Sandbox Code Playgroud)
但我想有像IIntX这将意味着IINT ň任何N.
可能吗?
在 Rust 中,一个明确的类型impl Trait for Object保证了它Object具有这一特性。现在,C++20 概念当然更加笼统一些,因为它们不仅仅与一种类型相关联,而且可能与多种类型相关联。然而,这引出了一个问题:如何确定您实现的某些类型实际上满足某些概念。
现在的概念有点鸭子式,如果你的对象满足了有人试图在一个requires块中用它做的所有事情(它像鸭子一样嘎嘎叫),那么它就会像鸭子一样传递并满足这个概念。但是有没有办法说:“我希望这个分类的类能够通过测试”?
例如,这可能看起来像这样:
class MyClass1 { ... }
class MyClass2 { ... }
template<typename T1, T2>
concept MyConcept = requires(T1 t1, T2 t2) { ... }
static_assert( satisfies<MyConcept, MyClass1, MyClass2>() )
Run Code Online (Sandbox Code Playgroud)
这样的函数是否satisfies存在,如果不存在:如何编写这样的satisfies函数?
如果您将对象传递到某些概念的实现是可选的库(例如,接受可能位于或不位于边界的组件并且仅对位于边界的对象执行某些计算的库),则 Ducktyping 可能还不够。
template <typename GeneralComponent>
void do_something_optionally(GeneralComponent component) {
if constexpr ( satisfies<isBorder, GeneralComponent>() ) {
do_border_calculation(component);
} else {
// don't do border calculation
}
}
do_border_calculation(isBorder …Run Code Online (Sandbox Code Playgroud) 该代码有效吗?
template<bool b>
struct s {
void f() const {
}
static void f() requires b {
}
};
void g() {
s<true>().f();
}
Run Code Online (Sandbox Code Playgroud)
clang 说是,但 gcc 说不
<source>: In function 'void g()':
<source>:10:20: error: call of overloaded 'f()' is ambiguous
10 | s<true>().f();
| ~~~~~~~~~~~^~
<source>:3:14: note: candidate: 'void s<b>::f() const [with bool b = true]'
3 | void f() const {
| ^
<source>:5:21: note: candidate: 'static void s<b>::f() requires b [with bool b = true]'
5 | …Run Code Online (Sandbox Code Playgroud) c++ ×10
c++-concepts ×10
c++20 ×5
duck-typing ×1
recursion ×1
std-ranges ×1
templates ×1
type-traits ×1
typedef ×1