C++ 小技巧


输出变量名、行号、值

#define dbg(x) (cout << __LINE__ << ": " << #x << "=" << x << endl)
  • #x会被编译器替换为变量x的名称,是一个字符串
  • __LINE__表示当前代码行
  • __file__表示文件名

实现元组解构、多值返回

通过tuple<...>实现

元组解构

int main() {
	int x;
	string y;
	tie(x, y) = make_tuple(5, "OK"); // tuple

	dbg(x);
	dbg(y);
}

多值返回

auto f() {
	return make_tuple(5, "OK");
}

int main() {
	int x;
	string y;
	tie(x, y) = f();

	dbg(x);
	dbg(y);
}

实现Kotlin中的Result

C语言的union

union testUnion {
	int value;
	bool error;
};

int main() {
	union testUnion r;
	r.value = 5;
	dbg(r.error); //未定义行为,根据内存模型应该是true
}

在C++中,用std::variant来实现更好的union

int main() {
	variant v;
	v = "Hello";
	dbg(get(v));
	// dbg(get(v)); // 抛出bad_variant_access异常
	v = 5;
	dbg(get(v));
}

所以Reslut相当于variant
但是有个头疼的问题:如何判断是T还是E
同kotlin一样,C++有会引发exception的std::get和返回null的std::get_if
如果想像Kotlin那样用fold得自己封装,可以参考std::visit

int main() {
	variant v;
	struct {
		void operator()(int) {
			std::cout << "int!\n";
		}
		void operator()(std::string const&) {
			std::cout << "string!\n";
		}
	} visitor;
	visit(visitor, v);
}

当然,这段代码不够"现代",这是一个更现代的版本:

int main() {
	variant v = "OK";

	auto visitor = [](auto& value) {
		if constexpr (is_convertible()) { //判断为string类型
			cout <<  "String: "  << value << endl;
		}
		else if constexpr (is_convertible()) { //判断为int类型
			cout << "Int: " << value << endl;
		}
	};
	visit(visitor, v);
}

他们是等价的,这样说明了匿名函数实际上就是重载operator()的struct

"魔法"

//摘自 CppReference,详细解析可以看@zwvista的博文: https://www.cnblogs.com/zwvista/p/9256655.html
template struct overloaded : Ts... { using Ts::operator()...; }; //将多个类(Ts...)的operator()通过继承合并到一个类中
template overloaded(Ts...)->overloaded; // 自动推断向导,帮助编译器根据 overloaded 构造器参数的类型来推导 overloaded 的模板参数类型

int main() {
	std::variant var;
	std::visit(overloaded{
		[](auto arg) { std::cout << arg << ' '; },
		[](double arg) { std::cout << std::fixed << arg << ' '; },
		[](const std::string& arg) { std::cout << arg << ' '; },
	}, var);
}

这个堪称完美,建议出个std::overloaded(逃