第二章 函数参数的占位符类型¶
目录¶
背景: 使用auto
和其他占位符来声明普通函数的参数.
- 分类型模板参数的扩展
lambda
模板
1. 什么是函数参数的占位符¶
占位符有点类似泛型,允许传递任意类型的参数
,方便用户定义各种接口, 在cpp20版本之前, 仅允许lambda
表达式, 在cpp20以后, 扩展到普通函数. 它的原理其实就是: 模板的快捷方式
:
template <typename T> void lambda(T arg) { fmt::print("arg: {}\n", arg); }
void lambda_example() {
lambda(42);
lambda("Hello, World!");
}
2. 为什么要将auto
作为参数¶
2.1 进行延迟类型检查¶
使用auto
作为参数, 实现具有循环依赖关系的代码会容易很多. 对于以下例子的循环依赖, 本质上就是利用模板的延迟实例化特性.
namespace circular_dependency {
namespace compile_error {
struct C2; // forward declaration
struct C1 {
void foo(const C2 &c2) const { // Ok
// c2.print(); // error: C2 is incomplete, [compile error]
}
void print() const { fmt::print("C1::print()\n"); }
};
struct C2 {
void foo(const C1 &c1) const { // Ok
c1.print(); // Ok
}
void print() const { fmt::print("C2::print()\n"); }
};
} // namespace compile_error
namespace no_compile_error {
struct C2; // forward declaration
struct C1 {
void foo(const C2 &c2) const; // Ok, forward declaration
void print() const { fmt::print("C1::print()\n"); }
};
struct C2 {
void foo(const C1 &c1) const {
c1.print(); // Ok, C1 is complete
}
void print() const { fmt::print("C2::print()\n"); }
};
inline void C1::foo(const C2 &c2) const {
c2.print(); // Ok
}
} // namespace no_compile_error
namespace no_compile_error_using_auto {
struct C2; // forward declaration
struct C1 {
void foo(const std::same_as<C2> auto &c2) const { c2.print(); }
void print() const { fmt::print("C1::print()\n"); }
};
struct C2 {
void foo(const std::same_as<C1> auto &c1) const { c1.print(); }
void print() const { fmt::print("C2::print()\n"); }
};
} // namespace no_compile_error_using_auto
} // namespace circular_dependency
void test_circular_dependency() {
circular_dependency::no_compile_error::C1 c1;
circular_dependency::no_compile_error::C2 c2;
c1.foo(c2); // Ok, C2 is forward declared
c2.foo(c1); // Ok, C1 is complete
circular_dependency::no_compile_error_using_auto::C1 c1_auto;
circular_dependency::no_compile_error_using_auto::C2 c2_auto;
c1_auto.foo(c2_auto); // Ok, C2 is forward declared
c2_auto.foo(c1_auto); // Ok, C1 is complete
}
2.2 为什么等价的函数模板需要显示调用¶
lambda函数可以被编译器推导出类型, 但函数模板不能自动作为函数指针进行传递, 必须显示指定模板参数
或包装成lambda
namespace auto_and_lambda {
bool lessByNameFunc(const auto &c1, const auto &c2) { return c1.id < c2.id; }
// 等价于
template <typename T1, typename T2>
bool lessByNameFunc2(const T1 &c1, const T2 &c2) {
return c1.id < c2.id;
}
void test_auto_and_lambda() {
struct Item {
int id;
};
std::vector<Item> items{Item{1}, Item{2}, Item{3}};
// std::sort(items.begin(), items.end(), lessByNameFunc); // compile error
std::sort(items.begin(), items.end(), lessByNameFunc<Item, Item>);
std::sort(items.begin(), items.end(), [](const auto &c1, const auto &c2) {
return c1.id < c2.id;
}); // lambda 可以被编译器推导出合适的类型,
// 并在lambda的上下文实例化这个函数对象
}
} // namespace auto_and_lambda
2.3 可变参数auto
¶
理解变参模板的auto
写法即可
namespace parameters_auto_pack {
void foo(auto... args) {
((fmt::print("{} ", args)), ...); // C++17 fold expression
fmt::print("\n");
}
template <typename... Args> void foo2(Args... args) {
((fmt::print("{} ", args)), ...); // C++17 fold expression
fmt::print("\n");
}
void test_parameters_auto_pack() {
foo(1, 2, 3);
foo("Hello", "World");
foo2(1, 2, 3);
foo2("Hello", "World");
}
} // namespace parameters_auto_pack
3. 完整示例代码¶
#include <fmt/core.h>
#include <algorithm>
#include <concepts>
#include <vector>
namespace less_cpp_20 {
auto lambda = [](auto arg) { fmt::print("arg: {}\n", arg); };
void lambda_example() {
lambda(42);
lambda("Hello, World!");
}
} // namespace less_cpp_20
namespace greater_cpp_20 {
void lambda(auto arg) { fmt::print("arg: {}\n", arg); }
void lambda_example() {
lambda(42);
lambda("Hello, World!");
}
} // namespace greater_cpp_20
namespace alias {
template <typename T> void lambda(T arg) { fmt::print("arg: {}\n", arg); }
void lambda_example() {
lambda(42);
lambda("Hello, World!");
}
} // namespace alias
namespace circular_dependency {
namespace compile_error {
struct C2; // forward declaration
struct C1 {
void foo(const C2 &c2) const { // Ok
// c2.print(); // error: C2 is incomplete, [compile error]
}
void print() const { fmt::print("C1::print()\n"); }
};
struct C2 {
void foo(const C1 &c1) const { // Ok
c1.print(); // Ok
}
void print() const { fmt::print("C2::print()\n"); }
};
} // namespace compile_error
namespace no_compile_error {
struct C2; // forward declaration
struct C1 {
void foo(const C2 &c2) const; // Ok, forward declaration
void print() const { fmt::print("C1::print()\n"); }
};
struct C2 {
void foo(const C1 &c1) const {
c1.print(); // Ok, C1 is complete
}
void print() const { fmt::print("C2::print()\n"); }
};
inline void C1::foo(const C2 &c2) const {
c2.print(); // Ok
}
} // namespace no_compile_error
namespace no_compile_error_using_auto {
struct C2; // forward declaration
struct C1 {
void foo(const std::same_as<C2> auto &c2) const { c2.print(); }
void print() const { fmt::print("C1::print()\n"); }
};
struct C2 {
void foo(const std::same_as<C1> auto &c1) const { c1.print(); }
void print() const { fmt::print("C2::print()\n"); }
};
} // namespace no_compile_error_using_auto
} // namespace circular_dependency
void test_print() {
less_cpp_20::lambda_example();
greater_cpp_20::lambda_example();
alias::lambda_example();
}
void test_circular_dependency() {
circular_dependency::no_compile_error::C1 c1;
circular_dependency::no_compile_error::C2 c2;
c1.foo(c2); // Ok, C2 is forward declared
c2.foo(c1); // Ok, C1 is complete
circular_dependency::no_compile_error_using_auto::C1 c1_auto;
circular_dependency::no_compile_error_using_auto::C2 c2_auto;
c1_auto.foo(c2_auto); // Ok, C2 is forward declared
c2_auto.foo(c1_auto); // Ok, C1 is complete
}
namespace auto_and_lambda {
bool lessByNameFunc(const auto &c1, const auto &c2) { return c1.id < c2.id; }
// 等价于
template <typename T1, typename T2>
bool lessByNameFunc2(const T1 &c1, const T2 &c2) {
return c1.id < c2.id;
}
void test_auto_and_lambda() {
struct Item {
int id;
};
std::vector<Item> items{Item{1}, Item{2}, Item{3}};
// std::sort(items.begin(), items.end(), lessByNameFunc); // compile error
std::sort(items.begin(), items.end(), lessByNameFunc<Item, Item>);
std::sort(items.begin(), items.end(), [](const auto &c1, const auto &c2) {
return c1.id < c2.id;
}); // lambda 可以被编译器推导出合适的类型,
// 并在lambda的上下文实例化这个函数对象
}
} // namespace auto_and_lambda
namespace parameters_auto_pack {
void foo(auto... args) {
((fmt::print("{} ", args)), ...); // C++17 fold expression
fmt::print("\n");
}
template <typename... Args> void foo2(Args... args) {
((fmt::print("{} ", args)), ...); // C++17 fold expression
fmt::print("\n");
}
void test_parameters_auto_pack() {
foo(1, 2, 3);
foo("Hello", "World");
foo2(1, 2, 3);
foo2("Hello", "World");
}
} // namespace parameters_auto_pack
int main() {
//
test_print();
test_circular_dependency();
auto_and_lambda::test_auto_and_lambda();
parameters_auto_pack::test_parameters_auto_pack();
return 0;
}