学习 Vulkan 也有一定时间了,也看一些 C++ 并行编程的书。结合这几个月对图形和 C++ 开源社区关注的体会。感觉现在是一个跨时代的节点,即串行向并行思想转变的时代。尤其是看完 C++ 之父 Bjarne Stroustrup 的论文《Thriving in a Crowded and Changing World: C++ 2006–2020》后,我更坚信我的观点!
想要跟上新时代,就要不断的接受新的思想和新的技术。所以,自己放弃了现在的工作,打算利用一些时间学习一下新技术,新思想。
先从 C++ 20 开始,就以 《Thriving in a Crowded and Changing World: C++ 2006–2020》为基础,系统的学习一下从 C++ 11 到 C++ 20 的主要特性。为迎接 C++ 23 做准备。
注意:关于 C++ 规范的内容笔记都是机翻+本人修正的。没有太多借鉴价值。都是长句,很难翻译!翻译是为了大致了解规范的内容,便于定位问题。定位后去详读规范文档。这样才能真正理解大佬设计规范的思想!
先从 Modules 出发,个人认为它是 C++20 最重要的内容之一。据说 C++ 23 所有标准库都要支持 Modules!
Translation unit #1:
export module A;
export import :Foo;
export int baz();
Translation unit #2:
export module A:Foo;
import :Internals;
export int foo() { return 2 * (bar() + 1); }
Translation unit #3:
module A:Internals;
int bar();
Translation unit #4:
module A;
import :Internals;
int bar() { return baz() - 10; }
int baz() { return 30; }
Translation unit #1:
module B:Y; // does not implicitly import B
int y();
Translation unit #2:
export module B;
import :Y; // OK, does not create interface dependency cycle
int n = y();
Translation unit #3:
module B:X1; // does not implicitly import B
int &a = n; // error: n not visible here
Translation unit #4:
module B:X2; // does not implicitly import B
import B;
int &b = n; // OK
Translation unit #5:
module B; // implicitly imports B
int &c = n; // OK
示例
Source file "a.h":
export int x;
Translation unit #1:
module;
#include "a.h" // error: declaration of x is not in the
// purview of a module interface unit
export module M;
export namespace {} // error: does not introduce any names
export namespace {
int a1; // error: export of name with internal linkage
}
namespace {
export int a2; // error: export of name with internal linkage
}
export static int b; // error: b explicitly declared static
export int f(); // OK
export namespace N { } // OK
export using namespace N; // error: does not declare a name
Source file "b.h":
int f();
Importable header "c.h":
int g();
Translation unit #1:
export module X;
export int h();
Translation unit #2:
module;
#include "b.h"
export module M;
import "c.h";
import X;
export using ::f, ::g, ::h; // OK
struct S;
export using ::S; // error: S has module linkage
namespace N {
export int h();
static int h(int); // #1
}
export using N::h; // error: #1 has internal linkage
export module M;
struct S;
export using T = S; // OK, exports name T denoting type S
export module M;
struct S { int n; };
typedef S S;
export typedef S S; // OK, does not redeclare an entity
export struct S; // error: exported declaration follows non-exported declaration
Interface unit of M:
export module M;
export struct X {
static void f();
struct Y { };
};
namespace {
struct S { };
}
export void f(S); // OK
struct T { };
export T id(T); // OK
export struct A; // A exported as incomplete
export auto rootFinder(double a) {
return [=](double x) { return (x + a/x)/2; };
}
export const int n = 5; // OK, n has external linkage
Implementation unit of M:
module M;
struct A {
int value;
};
Main program:
import M;
int main() {
X::f(); // OK, X is exported and definition of X is reachable
X::Y y; // OK, X::Y is exported as a complete type
auto f = rootFinder(2); // OK
return A{45}.value; // error: A is incomplete
}
Interface unit of M:
export module M;
static int f(); // #1
export int f(); // error: #1 gives internal linkage
struct S; // #2
export struct S; // error: #2 gives module linkage
namespace {
namespace N {
extern int x; // #3
}
}
export int N::x; // error: #3 gives internal linkage
export module M;
export namespace N {
int x; // OK
static_assert(1 == 1); // error: does not declare a name
}
module-import-declaration 只能出现在全局命名空间范围内
module-import-declaration 导入一组按如下所述确定的翻译单元
指定 module-name 为 M 的 module-import-declaration 导入 M 的所有模块接口单元
(4)指定 module-partition 的 module-import-declaration 只能出现在某个模块 M 的模块单元中的 module-declaration 之后
指定 header-name H 的 module-import-declaration 导入合成(synthesized )的 header unit,该单元是通过将翻译阶段(Phases of translation)应用于 H 指定的头文件和源文件形成的翻译单元, 这些头文件和源文件不应包含 module-declaration
importable header 是实现定义的头文件成员集合,其中包括所有可导入的 C++ 头文件。
尽管所有声明都被隐式导出,但在 header unit 中允许声明具有内部链接的名称
当一个 module-import-declaration 导入一个翻译单元 T 时,它还导入了 T 中由导出的 module-import-declarations 导入的所有翻译单元;
不得导出模块实现单元
Translation unit #1:
module M:Part;
Translation unit #2:
export module M;
export import :Part; // error: exported partition :Part is an implementation unit
不是 module partition 的 module M 的 module 实现单元不应包含指定 M 的 module-import-declaration
module M;
import M;
如果翻译单元包含导入 U 的声明(可能是 module-declaration),或者如果它有对 U 的接口依赖的翻译单元有接口依赖,则翻译单元对 U 具有接口依赖。
Interface unit of M1:
export module M1;
import M2;
Interface unit of M2:
export module M2;
import M3;
Interface unit of M3:
export module M3;
import M1;
即使在实例化上下文 includes module unit 的情况下,丢弃的声明对于 module unit 之外的名称查找,以及在其实例化节点位于 module unit 之外的模板实例化来说都是不可访问或不可见的
示例
const int size = 2;
int ary1[size]; // unspecified whether size is decl-reachable from ary1
constexpr int identity(int x) { return x; }
int ary2[identity(2)]; // unspecified whether identity is decl-reachable from ary2
template<typename> struct S;
template<typename, int> struct S2;
constexpr int g(int);
template<typename T, int N>
S<S2<T, g(N)>> f(); // S, S2, g, and :: are decl-reachable from f
template<int N>
void h() noexcept(g(N) == N); // g and :: are decl-reachable from h
Source file "foo.h":
namespace N {
struct X {};
int d();
int e();
inline int f(X, int = d()) { return e(); }
int g(X);
int h(X);
}
Module M interface:
module;
#include "foo.h"
export module M;
template<typename T> int use_f() {
N::X x; // N::X, N, and :: are decl-reachable from use_f
return f(x, 123); // N::f is decl-reachable from use_f,
// N::e is indirectly decl-reachable from use_f
// because it is decl-reachable from N::f, and
// N::d is decl-reachable from use_f
// because it is decl-reachable from N::f
// even though it is not used in this call
}
template<typename T> int use_g() {
N::X x; // N::X, N, and :: are decl-reachable from use_g
return g((T(), x)); // N::g is not decl-reachable from use_g
}
template<typename T> int use_h() {
N::X x; // N::X, N, and :: are decl-reachable from use_h
return h((T(), x)); // N::h is not decl-reachable from use_h, but
// N::h is decl-reachable from use_h
}
int k = use_h<int>();
// use_h is decl-reachable from k, so
// N::h is decl-reachable from k
Module M implementation:
module M;
int a = use_f<int>(); // OK
int b = use_g<int>(); // error: no viable function for call to g;
// g is not decl-reachable from purview of
// module M’s interface, so is discarded
int c = use_h<int>(); // OK
export module A;
export inline void fn_e(); // error: exported inline function fn_e not defined
// before private module fragment
inline void fn_m(); // OK, module-linkage inline function
static void fn_s();
export struct X;
export void g(X *x) {
fn_s(); // OK, call to static function in same translation unit
fn_m(); // OK, call to module-linkage inline function
}
export X *factory(); // OK
module :private;
struct X {}; // definition not reachable from importers of A
X *factory() {
return new X ();
}
void fn_e() {}
void fn_m() {}
void fn_s() {}
Translation unit #1:
export module stuff;
export template<typename T, typename U> void foo(T, U u) { auto v = u; }
export template<typename T, typename U> void bar(T, U u) { auto v = *u; }
Translation unit #2:
export module M1;
import "defn.h"; // provides struct X {};
import stuff;
export template<typename T> void f(T t) {
X x;
foo(t, x);
}
Translation unit #3:
export module M2;
import "decl.h"; // provides struct X; (not a definition)
import stuff;
export template<typename T> void g(T t) {
X *x;
bar(t, x);
}
Translation unit #4:
import M1;
import M2;
void test() {
f(0);
g(0);
}
f(0) 的调用是有效的; foo 的实例化上下文包括
f(0) 的节点g(0) 的调用是否有效:bar 的实例化上下文包括
g(0) 的节点如果 U 是包含 P 的 translation unit 具有接口依赖关系的 module interface unit,或者包含 P 的 translation unit 在 P 之前导入 U,则 translation unit U 必须从节点 P 访问
所有必须可访问的翻译单元都是可访问的。程序中的节点具有接口依赖关系的其他翻译单元可能被认为是可访问的,但未指定哪些是以及在什么情况下
如果对于实例化上下文中的任何节点 P,声明 D 是可访问的,则
上下文中实体的所有可访问声明的累积属性决定了该上下文中实体的行为
示例
Translation unit #1:
export module M:A;
export struct B;
Translation unit #2:
module M:B;
struct B {
operator int();
};
Translation unit #3:
module M:C;
import :A;
B b1; // error: no reachable definition of struct B
Translation unit #4:
export module M;
export import :A;
import :B;
B b2;
export void f(B b = B());
Translation unit #5:
module X;
import M;
B b3; // error: no reachable definition of struct B
void g() { f(); } // error: no reachable definition of struct B
即使名称查找不可见,实体也可以具有可访问的声明
示例
Translation unit #1:
export module A;
struct X {};
export using Y = X;
Translation unit #2:
module B;
import A;
Y y; // OK, definition of X is reachable
X x; // error: X not visible to unqualified lookup
传统 c++ 代码
公共头文件 #0:
#ifndef STRING_H
#define STRING_H
#define STRING_INCLUDED
#include "SPString.h"
#undef STRING_INCLUDED
#endif
------------------------------------
头文件 #1:
#pragma once
#include
using sp_string = std::string;
class SPString
{
public:
virtual bool is_empty() = 0;
virtual const sp_string& get_string() = 0;
};
SPString* Make_String();
------------------------------------
头文件 #2:
#pragma once
#include "SPInterfcae.h"
class SPStringInternal : public SPString
{
public:
virtual bool is_empty() override;
virtual const sp_string& get_string() override;
private:
sp_string str_;
};
------------------------------------
源文件 #3:
#include "SPStringInternal.h"
SPString* Make_String()
{
return new SPStringInternal();
}
bool SPStringInternal::is_empty()
{
return false;
}
const sp_string& SPStringInternal::get_string()
{
return str_;
}
------------------------------------
Modules (对比)
primary module interface unit #0
export module SPString;
export import :String;
------------------------------------
module interface unit #1:
module;
#include
export module SPString:String;
export using sp_string = std::string;
export
class SPString
{
public:
virtual bool is_empty() = 0;
virtual const sp_string& get_string() = 0;
};
export
SPString* Make_String();
------------------------------------
module interface unit #2:
export module SPString:String.Internal;
import :String;
class SPStringInternal : public SPString
{
public:
virtual bool is_empty() override;
virtual const sp_string& get_string() override;
private:
sp_string str_;
};
------------------------------------
module implementation unit #3:
module SPString;
import :String;
import :String.Internal;
SPString* Make_String()
{
return new SPStringInternal();
}
bool SPStringInternal::is_empty()
{
return false;
}
const sp_string& SPStringInternal::get_string()
{
return str_;
}
我是按照这种方式进行划分的,如果觉得这样不合理,欢迎指正!
调用方式
#include "SPString.h"
auto main() -> int {
auto str = Make_String();
str->get_string();
return 1;
}
import SPString;
auto main() -> int {
auto str = Make_String();
str->get_string();
return 1;
}
传统 c++ 风格的不足之处
#include 中的代码的含义#include 顺序相关Modules 的优势
Modules 不足(个人最近使用总结的观点)
module;#include 必须放置在 module; 之后 export module ***; 之前module: private;Modules 支持设置
注意事项
环境搭建
gcc 编译注意事项
环境搭建
创建 CMake 工程
Cmake 构建和编译注意事项
我个人是特别喜欢 Modules 的,毕竟解决我开发中三个最大的痛点