跟我学c++中级篇——再谈Concepts

C++20四大之三:concept特性详解 前言:C++模板的演进 模板的演进是C++发展史中一条十分重要的线,个人觉得Concept是这条线中最大的一个特性了。在介绍Concept之前,我们先捋一捋模板这条线的发展。 据Stroustrup先生回忆, 对模板的设想早在1982年便有了,正式提出是在1998年的 USENIX C++ conference会议上提出,设计模板的初衷是因为当时的C++缺少一个标准库,而当时没有模板的C++很难设计出“vector、list”这种适用于多种类型的容器。 到1998年模板正式进入标准,在这之前C++模板已经 阅读详情

一、理解Concepts

可能很多的c++程序员到职业生涯结束,都没有真正写过模板程序,有一些甚至都没有听说过模板。这个很正常,特别是一些参与c开发的c++程序员更是如此。不过,c++的标准迭代比较快,导致最近其又热了起来,或多或少的一些程序员开始看看模板这个东西。模板这个技术如果真的用得不是多复杂其实也没啥影响。但是,这玩意儿比较恶心的一点就是,出了错,报得错误非常的莫名其妙。这就导致可能一个很小的错误需要不断的去查找。
这个问题有些棘手,确实是。不光是普通程序员觉得如此,即使是大牛神牛们也觉得恶心。那就解决一下呗。SFINAE(Substitution Failure Is Not An Error,替换失败并非错误),这个机制在前面提到过,但是如果在网上搜索,发现这个玩意儿并不如其它的c++技术普遍。一般来说,简单好用的东西都会流行,简单不太好用的也会流行。但是,既不简单又不好用的东西,肯定不会流行。说得是谁,就不点名了,大家都明白。
正是因为模板匹配着这个SFINAE机制,这种纯技巧的东西,把非常多的学习c++的人劝退了。对,是劝退了。不要来学模板,不要使用SFINAE机制。可这不是大牛们想要的啊,他们想推广c++,所以Concepts就应运而生了,Concepts的概念和各种说明在前面说得很多了,这里就不再重复。
SFINAE机制除了有对模板参数进行约束和检查的功能外还能够对模板元编程进行支持,即对模板的编译期的自省(introspection)和反射(reflection)(静态反射和metaclass,其实就是RTTI,也就是c++原生缺少对类型的动态控制能力)。强大的Concepts会有更好的表现。
说明:其实c++是稍微有一些自省功能(静态反射),但是非常弱小(std::is_xxx系列),反射(动态反射)基本就没戏了,全靠技巧堆。
另外:静态反射一般指可以在编译时获得元数据也即对象的各个域(即类的各个成员),动态反射一般指根据名字生成并得到类对象,同时可以动态增加元数据。

二、Concepts用法

1、SFINAE机制
做为理解Concepts的过程,仍然要从SFINAE开始谈起,然后再谈一下requires,最后再引入concepts。SFINAE,可以理解成后面两个的基础版本,不好使,但确实好用。SFINAE机制可以对类型、表达式和偏特化等场景上进行检查和约束,特别是使用一些技巧,可以更好的在编译处理一些异常,能更好的解决一些模板的不明所以的错误和异常。一种常见手法,是在返回类型上使用表达式 SFINAE,其中表达式使用逗号运算符,其左子表达式是所检验的(转型到 void 以确保不会选择返回类型上的用户定义逗号运算符),而右子表达式具有期望函数返回的类型。这个在前面的“c++11中的declval和decltype”中例程的size函数中也用到了。
下面看一个c++17中和c++20中的对比,使用SFINAE和Concepts:

//c++20
template <typename T>
requires (sizeof(T) <= 2)
struct Example{};
//c++17
template <typename T, typename = std::enable_if_t<sizeof(T) <= 2, void>>
struct Example{};

2、requires约束

而requires,按照说明有两种情况即requires子句(requires-clause)和requires表达式。其主要的形式如下:

产生一个描述约束的 bool 类型的纯右值表达式,形式如下:
requires { 要求序列 }	//序列,就是一组需求	
requires ( 参数列表(可选) ) { 要求序列 }		
参数列表:一个用逗号分隔的参数列表,就像在函数声明中一样,但不允许使用默认参数,而且不能以省略号结尾(除了一个表示参数包的省略号)。这些参数没有存储周期、链接性质或生命周期,只用于协助指定要求。这些参数的作用域直到要求序列的结束符 }。
要求序列:要求序列,每一个要求都是如下形式的一种:
简单要求,类型要求,复合要求,嵌套要求

requires其实是形成一种约束机制。
先看一下requires的表达式如何使用:
需要说明的是只要不是requires冲突的表达式,都可以使用。看一下示例:

//简单需求
template<typename T>
concept Addable = requires (T a, T b)
{
    a + b; // "需要表达式 a+b 被编译为有效的表达式"
};
 
template<class T, class U = T>
concept Swappable = requires(T&& t, U&& u)
{
    swap(std::forward<T>(t), std::forward<U>(u));
    swap(std::forward<U>(u), std::forward<T>(t));
};

//类型需求
template<typename T>
using Ref = T&;
 
template<typename T>
concept C = requires
{
    typename T::inner; // 需要嵌套成员名
    typename S<T>;     // 需要类模板特化
    typename Ref<T>;   // 需要别名模板替换
};
 
template<class T, class U>
using CommonType = std::common_type_t<T, U>;
 
template<class T, class U>
concept Common = requires (T&& t, U&& u)
{
    typename CommonType<T, U>; // CommonType<T, U> 是一个合法的类型名
    { CommonType<T, U>{std::forward<T>(t)} }; 
    { CommonType<T, U>{std::forward<U>(u)} }; 
};

//复合要求
//一个复合要求具有如下形式
//{ 表达式 } 可选的noexcept -> concept名 可选的<参数列表>
template<typename T>
concept C2 = requires(T x)
{
    // 表达式 *x 必须合法
    // 并且 类型 T::inner 必须存在
    // 并且 *x 的结果必须可以转换为 T::inner
    {*x} -> std::convertible_to<typename T::inner>;
 
    // 表达式 x + 1 必须合法
    // 并且 std::same_as<decltype((x + 1)), int> 必须满足
    // 即, (x + 1) 必须为 int 类型的纯右值
    {x + 1} -> std::same_as<int>;
 
    // 表达式 x * 1 必须合法
    // 并且 它的结果必须可以转换为 T
    {x * 1} -> std::convertible_to<T>;
};
//嵌套需求
//一个嵌套需求具有如下形式
//requires 约束表达式 ;		
template<class T>
concept Semiregular = DefaultConstructible<T> &&
    CopyConstructible<T> && Destructible<T> && CopyAssignable<T> &&
requires(T a, size_t n)
{  
    requires Same<T*, decltype(&a)>; // 嵌套:"Same<...> 被求值为真"
    { a.~T() } noexcept; // 复合:"a.~T()" 是一个不会抛出的合法表达式
    requires Same<T*, decltype(new T)>; // 嵌套:"Same<...> 被求值为真"
    requires Same<T*, decltype(new T[n])>; // 嵌套
    { delete new T }; // 复合
    { delete new T[n] }; // 复合
};

其实嵌套需求可以理解成requires子句,requires关键字用来引入 requires 子句,它指定对各模板实参,或对函数声明的约束。
这种情况下,关键词 requires 必须后随某个常量表达式(因此可以写成 requires true),但这是为了使用一个具名概念(如上例),具名概念的一条合取/析取,或者一个 requires 表达式。
表达式必须具有下列形式之一:
1)初等表达式,例如 Swappable、std::is_integral::value、(std::is_object_v && …) 或任何带括号的表达式
2)以运算符 && 联结的初等表达式的序列
3)以运算符 || 联结的前述表达式的序列

再看一下requires的子句如何使用:

template<typename T>
concept Addable = requires (T x) { x + x; }; // requires 表达式
 
template<typename T> requires Addable<T> // requires 子句,不是 requires 表达式
T add(T a, T b) { return a + b; }
 
template<typename T>
    requires requires (T x) { x + x; } // 临时约束,注意关键字用了两次
T add(T a, T b) { return a + b; }

再看一个在前面的文档中提到过的:

template<typename T, typename U>
     requires requires (T t, U u) {  t + u; }//按Stackflow上说,第一个是子句后跟bool值,第二个是表达式,产生bool值
auto addTest(T t, U u)
{
    return t + u;
}

这和才举的例子中的最后一个基本一样。其实这些官网的例子不如一些网上博友们的例子更容易说明问题,但是那些容易说明的例子又不容易看到应用的全过程。所以,此处还是用了这些官网的例子。

3、concepts(概念)
concepts这个作用是什么?在模板编程中经常会遇到这种问题:模板本身,理论上是匹配任何类型的。但是,往往在实际情况中,上面的分析是一种理想状态。可能有的开发者忘记某一项要求,亦或者某个类型不能使用这个模板,诸如此类。换句话说,在模板实例化或者特化的过程中,如何处理不符合条件的情况。文章开头也提到了SFINAE机制,但在c++20,引入了更强大的concepts。其实不管是哪种机制,都可以把其当成一种对模板使用过程中对条件限制状态的一种控制。当不符合条件的状态出现时,限制其继续使用。
Concept语句需要书写在模板参数之后,模板实体之前,并且需要跟在requires关键字之后,它的行为模式为:


template < 模板形参列表 >
concept 概念名 属性(可选) = 约束表达式(bool类型);

一个最基本的例子:

// 概念
template<class T, class U>
concept Derived = std::is_base_of<U, T>::value;

需要说明的是,不光是看到concepts这才是concepts,前面提到的requires同样也属于这个范畴。
在concepts可以使用一个bool序列,用分号隔开,前面有好多例子了,再看一个:

template <typename T>
requires requires {
  std::is_trivial_v<T>;//T是否平凡
  sizeof(T) <= sizeof(void *);
} || std::is_trivially_copyable_v<T>//平凡可复制

concepts也可以独立成一块来实现,看下面的例子:

// 概念 "Hashable" 的声明可以被符合以下条件的任意类型 T 满足:
// 对于 T 类型的值 a,表达式 std::hash<T>{}(a) 可以编译并且它的结果可以转换到 std::size_t
template<typename T>
concept Hashable = requires(T a)
{
    { std::hash<T>{}(a) } -> std::convertible_to<std::size_t>;
};

概念不能递归地提及自身,而且不能受约束:

template<typename T>
concept V = V<T*>; // 错误:递归的概念
 
template<class T>
concept C1 = true;
template<C1 T>
concept Error1 = true; // 错误:C1 T 试图约束概念定义
template<class T> requires C1<T>
concept Error2 = true; // 错误:requires 子句试图约束概念

概念不能被显式实例化、显式特化或部分特化(不能更改约束的原初定义的含义)。概念可以在标识表达式中命名。该标识表达式的值在满足约束表达式时是 true,否则是 false。概念在作为以下内容的一部分时也可以在类型约束中被命名:
1)类型模板形参声明
2)占位类型说明符
3)复合要求
概念在 类型约束 中接受的实参要比它的形参列表要求的要少一个,因为按语境推导出的类型会隐式地用作第一个实参:

template<class T, class U>
concept Derived = std::is_base_of<U, T>::value;
 
template<Derived<Base> T>
void f(T); // T 被 Derived<T, Base> 约束

concept本身就是一个静态布尔类型了,所以不需要取value,这比上一个版本c++17中还需要定义value并取值相对简单了不少。一些_t,_v结尾的函数就可以直接忽略到了。也就是说不用再考虑萃取的结果类型和静态类型了。

三、约束(Constraint)

1、定义说明
在c++20中还提到了约束的概念:约束是逻辑操作和操作数的序列,它指定了对模板实参的要求。它们可以在 requires 表达式(见下文)中出现,也可以直接作为概念的主体。可以认为是概念的应用。
在文档是这样描述的:“
有三种类型的约束:
1)合取(conjunction)
2)析取(disjunction)
3)不可分割约束(atomic constraint)
对包含遵循以下顺序的操作数的逻辑与表达式进行规范化,确定与一个声明关联的约束:
每个声明中受约束的类型模板形参或带占位类型声明的非类型模板形参所引入的约束表达式,按出现顺序;
模板形参列表之后的 requires 子句中的约束表达式;
简写函数模板声明中每个拥有受约束占位类型的形参所引入的约束表达式;
尾部的 requires 子句中的约束表达式。
这个顺序决定了在检查是否满足时各个约束时的实例化顺序。”

2、具体说明
说明:本段落内容来自https://zh.cppreference.com/w/cpp/language/constraints#Requires_clauses

  1. 受约束的声明只能以相同的语法形式重声明。不要求诊断:
template<Incrementable T>
void f(T) requires Decrementable<T>;
 
template<Incrementable T>
void f(T) requires Decrementable<T>; // OK:重声明
 
template<typename T>	
    requires Incrementable<T> && Decrementable<T>
void f(T); // 非良构,不要求诊断
 
// 下列两个声明拥有不同的约束:
// 第一个声明拥有 Incrementable<T> && Decrementable<T>
// 第二个声明拥有 Decrementable<T> && Incrementable<T>
// 尽管它们在逻辑上等价
 
template<Incrementable T> 
void g(T) requires Decrementable<T>;
 
template<Decrementable T> 
void g(T) requires Incrementable<T>; // 非良构,不要求诊断

  1. 合取
    两个约束的合取是通过在约束表达式中使用 && 运算符来构成的:
template<class T>
concept Integral = std::is_integral<T>::value;
template<class T>
concept SignedIntegral = Integral<T> && std::is_signed<T>::value;
template<class T>
concept UnsignedIntegral = Integral<T> && !SignedIntegral<T>;

两个约束的合取只有在两个约束都被满足时才会得到满足。合取从左到右短路求值(如果不满足左侧的约束,那么就不会尝试对右侧的约束进行模板实参替换:这样就会防止出现立即语境外的替换所导致的失败)。

template<typename T>
constexpr bool get_value() { return T::value; }
 
template<typename T>
    requires (sizeof(T) > 1 && get_value<T>())
void f(T);   // #1
 
void f(int); // #2
 
void g() 
{
    f('A'); // OK,调用 #2。当检查 #1 的约束时,
            // 不满足 'sizeof(char) > 1',故不检查 get_value<T>()
}
  1. 析取
    两个约束的析取,是通过在约束表达式中使用 || 运算符来构成的:
    如果其中一个约束得到满足,那么两个约束的析取的到满足。析取从左到右短路求值(如果满足左侧约束,那么就不会尝试对右侧约束进行模板实参替换)。
template<class T = void>
    requires EqualityComparable<T> || Same<T, void>
struct equal_to;

  1. 不可分割约束
    不可分割约束由一个表达式 E,和一个从 E 内出现的模板形参到(对受约束实体的各模板形参的有所涉及的)模板实参的映射组成。这种映射被称作形参映射。
    不可分割约束在约束规范化过程中形成。E 始终不会是逻辑与(AND)或者逻辑或(OR)表达式(它们分别构成析取和合取)。
    对不可分割约束是否满足的检查会通过替换形参映射和各个模板实参到表达式 E 中来进行。如果替换产生了无效的类型或表达式,那么约束就没有被满足。否则,在任何左值到右值转换后,E 应当是 bool 类型的纯右值常量表达式,当且仅当它求值为 true 时该约束得以满足。
    E 在替换后的类型必须严格为 bool。不能有任何转换:
template<typename T>
struct S
{
    constexpr operator bool() const { return true; }
};
 
template<typename T>
    requires (S<T>{})
void f(T);   // #1
 
void f(int); // #2
 
void g()
{
    f(0); // 错误:检查 #1 时 S<int>{} 不具有 bool 类型,
          // 尽管 #2 能更好地匹配
}

如果两个不可分割约束由在源码层面上相同的表达式组成,且它们的形参映射等价,那么认为它们等同。

template<class T>
constexpr bool is_meowable = true;
 
template<class T>
constexpr bool is_cat = true;
 
template<class T>
concept Meowable = is_meowable<T>;
 
template<class T>
concept BadMeowableCat = is_meowable<T> && is_cat<T>;
 
template<class T>
concept GoodMeowableCat = Meowable<T> && is_cat<T>;
 
template<Meowable T>
void f1(T); // #1
 
template<BadMeowableCat T>
void f1(T); // #2
 
template<Meowable T>
void f2(T); // #3
 
template<GoodMeowableCat T>
void f2(T); // #4
 
void g()
{
    f1(0); // 错误,有歧义:
           // BadMeowableCat 和 Meowable 中的 is_meowable<T>
           // 构成了有区别的不可分割约束且它们并不等同(因此它们不互相包含)
 
    f2(0); // OK,调用 #4,它比 #3 更受约束
           // GoodMeowableCat 从 Meowable 获得其 is_meowable<T>
}
  1. 约束规范化
    约束规范化是将一个约束表达式变换为一个不可分割约束的合取与析取的序列的过程。表达式的范式定义如下:

表达式 (E) 的范式就是 E 的范式;
表达式 E1 && E2 的范式是 E1 和 E2 范式的合取;
表达式 E1 || E2 的范式是 E1 和 E2 范式的析取;
表达式 C<A1, A2, … , AN>(其中 C 指名某个概念)的范式,是以 A1, A2, … , AN 对 C 的每个不可分割约束的形参映射中的 C 的对应模板形参进行替换之后,C 的约束表达式的范式。如果在这种形参映射中的替换产生了无效的类型或表达式,那么程序非良构,不要求诊断。

template<typename T>
concept A = T::value || true;
 
template<typename U> 
concept B = A<U*>; // OK:规范化为以下各项的析取
                   // - T::value(映射为 T -> U*)和
                   // - true(映射为空)。
                   // 映射中没有无效类型,尽管 T::value 对所有指针类型均非良构
 
template<typename V> 
concept C = B<V&>; // 规范化为以下的析取
                   // - T::value(映射为 T-> V&*)和
                   // - true(映射为空)。
                   // 映射中构成了无效类型 V&* => 非良构,不要求诊断

任何其他表达式 E 的范式是一条不可分割约束,它的表达式是 E 而它的形参映射是恒等映射。这包括所有折叠表达式,甚至包括以 && 或 || 运算符进行的折叠。
用户定义的 && 或 || 重载在约束规范化上无效。

3、使用的形式

约束的使用有下面四种情况:

1)模板参数前的concept:

template<C1 T> 
void f(T);
2)模板参数列表后的requires子句:

template<typename T>
    requires C1<T>
void f(T);
3)函数模板使用auto替代模板类型时,其前面concept:

void f(C1 auto _arg);
c++20后模板类型可为auto,前面也提到过c++20泛型lambda同样支持使用auto。

4)在函数声明最后的requires子句:

template<typename T>
void f(T) requires C1<T>;

5)以上requirements同时存在:

template<C1 T>
    requires C2<T>
void f(T) requires C3<T>;

四、例程

看完了上面分析,再来一个具体的例程看一下,就更清楚了:

#include <string>
#include <cstddef>
#include <concepts>
 
// 概念 "Hashable" 的声明可以被符合以下条件的任意类型 T 满足:
// 对于 T 类型的值 a,表达式 std::hash<T>{}(a) 可以编译并且它的结果可以转换到 std::size_t
template<typename T>
concept Hashable = requires(T a)
{
    { std::hash<T>{}(a) } -> std::convertible_to<std::size_t>;
};
 
struct meow {};
 
template<Hashable T>
void f(T); // 受约束的 C++20 函数模板
 
// 应用相同约束的另一种方式:
// template<typename T>
//     requires Hashable<T>
// void f(T); 
// 
// template<typename T>
// void f(T) requires Hashable<T>; 
 
int main()
{
    using std::operator""s;
 
    f("abc"s); // OK,std::string 满足 Hashable
    f(meow{}); // 错误:meow 不满足 Hashable
}

在编译过程中会报:““f”: 未满足关联约束 TestConcepts”和““f”: 未找到匹配的重载函数”两个错误。
在前面提到过,STL中定义了许多concept,主要在、和几个头文件中。而在<type_traits>中定义了很多is_开头的类型,它们虽然名字不同,但意义基本一样。

五、总结

做为C++20 的四大特性(concept、module、range、coroutine))之一,concept重要性可以说非比一般。这里只是总体的说明了一下,再后面的应用中,会不断的对他进行展开分析。
c++的进步是有目共睹的,但这种进步能否满足在激烈的竞争下,争取c++更大生存空间,还是需要拭目以待。前些天看网上说c++超过了Java重新回到了第三名,说明还是有些作用的。但时间得放长了看,c++的进步还是得需要加速。一个是让c++在编程上的难度在现有基础上尽量减化;另外一个是不断的整合其它语言的和实际需要的新功能。这本身就是一个矛盾,但还得必须解决,这就需要c++标准委员会的大牛们多费心了。

C++20中的概念(Concepts)到底是什么概念? C++20中的Concepts为模板编程带来了巨大的提升,让模板代码变得更加清晰、易读且易于维护。通过Concepts,我们可以明确指定模板参数应满足的要求,避免了在编译时因为不匹配的类型而报出一堆晦涩难懂的错误信息。 阅读详情

相关推荐

C++ 的本质·第8篇 现代 C++ 的三大支柱:Concepts、Ranges、Coroutines

C++20 的发布,标志着 C++ 彻底告别了 C++98/03 的遗留问题,进入了一个的新时代。Concepts、Ranges、Coroutines 这三大支柱,解决了困扰 C++ 开发者数十年的痛点,使 C++ 从“能用”进化为“好用”。

腾TO的技术博客 778

C++20之Concept(概念部分,之一)

本篇介绍了C++20中的一个重要概念——concept

fl2011sx的博客 3844

C++(20):concept概念

编译错误:error: request for member 'p' in 't', which is of non-class type 'int'C++20引入了concept,其一个主要的作用是用于模板对其参数所需满足的条件进行约束和说明。当模板实参的类型有成员函数p的时候,可以很好的工作。test的参数1是个整数,没有成员函数p。可以看到这样的约束更加的直观。

风静如云的博客 726

Concepts (C++20)

Concepts (C++20 Concepts)复习

C/C++ 1646

Python入门基础教程(非常详细)

Python是一门开源免费、通用型的脚本编程语言,它上手简单,功能强大,坚持「极简主义」。Python类库(模块)极其丰富,这使得Python几乎无所不能,不管是传统的Web开发、PC软件开发、Linux运维,还是当下火热的机器学习、大数据分析、网络爬虫,Python都能胜任。这套Python基础教程不是教科书,不会玩弄概念,而是力求口语化和通俗化,让读者尽快入门。如果有小朋友出于兴趣学习Python,请引导他阅读本教程的前半部分,这是基础,可以降低学习成本;...

Yuki1127918的博客 34万+

安鸾渗透测试平台-CTF系列-bash

工具Xshell 公网VPS接收反弹shell Xshell新手的下载及安装教程(超详细)_新手程序员攀爬之路的博客-优快云博客_xshell使用教程 Xshell连接后 #!/bin/bash cat ${0} while : do echo "$" read input output=`$input` 显示了一个文件,不过咋做呢?这里可能考察的是linux的语法 方法 输入bash 获得shell cat ${0} while : do .

AAAAAAAAAAAA66的博客 2689

跟我学c++中级篇——concepts的几个应用

concepts的应用进行分析说明

fpcc的专栏 863

【推荐100个unity插件之34】在unity中实现和Live2D虚拟人物的交互——Cubism SDK for Unity

Live2D是一款2D建模的软件,常用于游戏,动态壁纸,虚拟主播,游戏公司里面是有专门的ive2D岗位的。游戏里面还有一个经常使用的软件——spine,他做的也是2d的模型,但是他更适合做一些动作,Live2D更适合人物交互方面。

向宇的博客,专注php/web全栈 unity游戏开发,欢迎大家评论纠错 4999

跟我学C++中级篇——Concepts的循环依赖

本文探讨了C++20概念(Concepts)中可能出现的循环依赖问题。文章指出,循环依赖在复杂模板编程中可能出现,表现为多个概念间的相互约束,类似于数学定理间的循环证明。通过示例代码展示了概念A依赖B、B又依赖A的典型循环场景。为解决这一问题,提出了三种设计方法:高层抽象、减少互相约束、分解复杂概念;以及两种编程技巧:使用前向声明和延迟加载约束检查。最后强调良好的设计思想是避免概念循环依赖的关键,编程技巧只是辅助手段。全文为处理C++概念循环依赖提供了实用指导。

fpcc的专栏 543

zabbix系列:内存VM.MEMORY.SIZE监控之total、free、pused、available 参数说明

free -m buffer是即将要被写入磁盘的,而cache是被从磁盘中读出来的. zabbix Web total:1046/1024=1.02G Available:216/1046=20.65% zabbix参数 total- 总物理内存. free- 可用内存. active- 内存当前使用或最近使用,所以它在RAM中。 inactive- 未使用内存. wired- 被标记为始终驻留在RAM中的内...

NIO4444 5668

大疆A型板使用经验分享(二)——A型板原理图和引脚图

一.原理图 二.引脚图

weixin_43361652的博客 7684

跟我学C++中级篇——编译期的条件选择

对编译期时C++编译器各种条件选择的处理

fpcc的专栏 222

lammps教程:非晶sio2建模方法

大家好,我是小马老师。 本文介绍非晶sio2的建模方法。 sio2的建模方法有很多,可以下载cif文件使用atomsk转为data文件,也可以直接在ms中导入sio2结构,这些方法建的sio2都是标准的sio2晶体。 如果要建立非晶状态的sio2,就是把排列规则的sio2变成无序排列的sio2。 非晶建模方法有很多,在lammps中,可以通过高温高压的方法打乱sio2的排列实现非晶建模。 代码中的温度和步数可根据需要自己调整。 下面给出in文件案例代码。 units metal atom

lammps_jiayou的博客 5268

C++20 Concepts

vs2019 C++20 Concepts01 concepts及concepts库函数02 Concepts demo 01 concepts及concepts库函数 什么是C ++概念? 概念(concepts)提供基础语言概念的定义,它们能用于进行模板实参的编译时校验,以及基于类型属性的函数派发。这些概念在程序中提供等式推理的基础。1 通过使用concepts,传统的模板元编程方面关于编译错...

一朵花开的时间 4511

试读源码在这里下载

试读文章的源码在这里下载。源码不做任何修改,重点重点重点:为了代码运行不报错,安装matlab 版本为 2021 及以上版本。matlab安装教程大家可以参考这个教程:手把手教你安装matlab软件。

在职AI算法工程师,擅长计算机视觉,YOLO目标检测、分割等,擅长web、pyqt界面可视化,好内容持续更新中,来这里跟大家一起学习,共同进步 4306

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

跟我学C++中级篇—编译期检查

对编译期的相关检查进行分析说明

fpcc的专栏 350

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

C++ 20新特性之Concepts

在C++ 20之前,我们在编写泛型代码时,模板参数的约束往往通过复杂的SFINAE(Substitution Failure Is Not An Error)策略或繁琐的Traits类来实现。这不仅难以阅读,也非常容易出错,导致很多程序员在提及泛型编程时,总是心有余悸、脊背发凉。

hope_wisdom的技术博客 2403

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

c++ concepts

C++ Concepts 的定义方式是通过concept关键字来声明一个概念。概念本质上是一个布尔表达式,用于描述模板参数需要满足的约束条件。基于类型特征的定义:可以使用标准库中的类型特征(如等)来定义概念。例如:template <typename T>这个概念Integral约束模板参数T必须是整数类型。如果T不是整数类型,编译器会报错。基于成员函数的定义:可以约束模板参数必须具有某些成员函数。例如:template <typename T>这个概念Container。

qq_62784677的博客 1177

跟我学C++中级篇——类型检测和安全控制

本文系统梳理了C++中类型检测与安全控制的技术方法。编译期技术包括SFINAE、标准库元编程接口、C++20概念、自定义模板及断言等;运行期技术涵盖RTTI、接口限制、容器检测和类型转换等。文章分析了各类技术的优缺点:编译期方法功能强大但复杂度高,运行期方法简单但扩展性差。在应用场景上,编译期技术适合基础库开发,运行期技术更适合常规开发。通过实例代码展示了运行时类型检测和编译期模板检测的具体实现,为开发者选择合适的技术方案提供了参考指南。

fpcc的专栏 743

C++20 Concepts简介

简要介绍c++20 Concept

一些C++相关的技术笔记或者教程. 961

跟我学c++中级篇——模板的基础术语说明

这些术语有些是比较为通用的,有的是比较少用的,但在某些书籍中又比较权威,所以了解一些还是有用的。特别是侯捷老师的书里,用法有些不同大家可以对比来印证就知道了什么意思。这里面其实每个都可以单独拿出来讲很多,本篇主要是综合一下,做一个纲领性的说明。以后如果标准继续更迭,可能会继续在此基础上不断的完善。

fpcc的专栏 581

跟我学c++中级篇——类型擦除应用的优化

对类型擦除应用中的一些具体的问题进行优化

fpcc的专栏 666

跟我学C++中级篇——is_detected的手动实现

本文介绍了C++中的"检测习惯用法"(detection idiom)技术,该技术由Walter E. Brown提出,用于在编译期检测类型特性是否存在或表达式是否有效。虽然is_detected未能进入C++17标准库,但其功能已被std::void_t和C++20概念部分替代。文章详细分析了实验库中的is_detected定义及其三种主要用途:检测类型特性、验证表达式有效性和控制编译分支。通过示例代码展示了如何检测类的复制赋值操作符是否存在等实际应用,并提供了手动实现的代码方案,包括

fpcc的专栏 646

跟我学c++高级篇——模板元编程之六SFINAE

SFINAE,对于想在模板编程中有更深的想法时,建议还是要认真学习。其实SFINAE更像是模板编程中更进阶的一种用法,虽然它可能在复杂的应用情况有些晦涩难懂,但既然决定想搞c++的模板编程,一些精深的东西还是需要去学习的。特别之于模板的元编程,更是如此。虽然有了concepts,但看以前的代码还是需要懂得这个技术的。

fpcc的专栏 4768

跟我学C++编程中级篇——std::enable_if的使用

对于SFINAE(不清楚的可以翻一下以前的相关文章)开发者来说,std::enable_if是绕不开的一个小话题。在C++11至C++20间,其在模板的元编程中起到了重要的作用。不过在C++20后Cocepts可以实现更清晰更简单的方式。其实非常好理解,这个模板结构体的第一个参数是bool型,第二个默认是个void,就是说,第一个参数决定了是否启用第二个参数。这下就明白了吧。struct T {T(Floating) : m_type(float_t) {} // 错误:无法被重载。

fpcc的专栏 2212
上一篇: 跟我学c++中级篇——迟延计算
下一篇: c++17中的apply和make_from_tuple
fpcc
fpcc 新星创作者: C/C++技术领域 新星创作者: C/C++技术领域
博客等级 码龄22年 4041粉丝 1190原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值