关于std::optional传递开销的讨论与优化
在讨论std::optional之前,我们应该先适当谈论一下“可空类型”。
我们知道,在传统的C++中,是不存在现代编程语言中常见的“可空类型”(如C#中的Nullable
1  | //C#  | 
显而易见,传统C++使用标记值来对某一变量设置为空的行为相比现代语言是存在问题、丑陋且不安全的:
- 某些情况下为了考虑标记值不得不放弃最为合适的数据类型(比如unsigned int之于size)
 - 其他使用者容易忘记甚至不了解标记值的含义
 - ……
 
std::optional
为了解决这一问题,C++17中引入了std::optional,实现了安全的可空值。
1
std::optional<unsigned int> size = std::nullopt;  //通过std::nullopt将size设置为空
1
void Func(const std::string& str);  //引用传递,不会发生拷贝,当Func不改变str时将其修饰为const
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19//测试类
class Test
{
public:
	std::string s = "Fuxk Cpp.";
	Test()
	{ std::cout << "\tctor\n"; }
	~Test() 
	{ std::cout << "\tdtor\n"; }
	Test(const Test&) 
	{ std::cout << "\tcopy ctor\n"; }
	Test(Test&&) noexcept
	{ std::cout << "\tmove ctor\n"; }
	Test& operator=(const Test&) 
	{ std::cout << "\tcopy op= \n"; return *this; }
	Test& operator=(Test&&) noexcept
	{ std::cout << "\tmove op= \n"; return *this; }
};1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31//测试函数
void OptFunc(std::optional<Test> x)
{ 
	std::cout << std::format("\t{}\n", x->s);
}
void DefaultFunc(const Test& x) 
{ 
	std::cout << std::format("\t{}\n", x.s);
}
//测试代码
int main()
{
	Test t0;
	std::cout << "-----------------------------------------\n";
	std::cout << "Use Optional:\n";
	std::cout << "copy:\n";
	OptFunc(t0);
	std::cout << "-------------\n";
	std::cout << "move:\n";
	OptFunc(Test());
	std::cout << "-----------------------------------------\n";
	std::cout << "Not use Optional:\n";
	std::cout << "copy:\n";
	DefaultFunc(t0);
	std::cout << "-----------------\n";
	std::cout << "move:\n";
	DefaultFunc(Test());
	std::cout << "-----------------------------------------\n";
	return 0;
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25        ctor
-----------------------------------------
Use Optional:
copy:
        copy ctor
        Fuxk Cpp.
        dtor
-------------
move:
        ctor
        move ctor
        Fuxk Cpp.
        dtor
        dtor
-----------------------------------------
Not use Optional:
copy:
        Fuxk Cpp.
-----------------
move:
        ctor
        Fuxk Cpp.
        dtor
-----------------------------------------
        dtor1
void OptFunc(const std::optional<Test>& x);  //这样?
1
void OptFunc(std::optional<const Test&> x);
这样一来好像走到了死胡同。但C++还是给我们留了一个缺口,即std::reference_wrapper。
>
std::reference_wrapper是包装引用于可复制、可赋值对象的类模板。它常用作将引用存储入无法正常保有引用的标准容器(类似std::vector)的机制。
可用T类型的std::reference_wrapper的optional保有引用。
通过reference_wrapper,我们可以做到等同于std::optional<const
Test&>的效果,我们添加一个函数并编写新的测试代码: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18//新添加测试函数
void OptWithRefWrapperFunc(std::optional<std::reference_wrapper<const Test>> x)
{
	std::cout << std::format("\t{}\n", x->get().s);
}
//新添加测试代码
int main()
{
	//...
	std::cout << "Use Optional with std::reference_wrapper:\n";
	std::cout << "copy:\n";
	OptWithRefWrapperFunc(t0);
	std::cout << "-----------------\n";
	std::cout << "move:\n";
	//FuncOptWithRefWrapper(Test());
	std::cout << "-----------------------------------------\n";
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31        ctor
-----------------------------------------
Use Optional:
copy:
        copy ctor
        Fuxk Cpp.
        dotr
-------------
move:
        cotr
        move cotr
        Fuxk Cpp.
        dtor
        dtor
-----------------------------------------
Not use Optional:
copy:
        Fuxk Cpp.
-----------------
move:
        ctor
        Fuxk Cpp.
        dtor
-----------------------------------------
Use Optional with std::reference_wrapper:
copy:
        Fuxk Cpp.
-----------------
move:
-----------------------------------------
        dtor1
2
3
4
5std::cout << std::format("{}, {}\n", sizeof(const Test*), 
			sizeof(std::optional<std::reference_wrapper<const Test>>));
//output:
//x86: 4, 8
//x64: 8, 16
Reference
https://abseil.io/tips/171 https://zh.cppreference.com/w/cpp/utility/optional https://stackoverflow.com/a/47842325/12822957 https://abseil.io/tips/163 https://zh.cppreference.com/w/cpp/utility/functional/reference_wrapper
原文链接: https://zhuanlan.zhihu.com/p/438821425