[英]Usage of empty structs in C++
在我正在阅读的一些代码中,我发现了空结构的用法,如下所示:
struct input_iterator_tag { };
struct bidirectional_iterator_tag { };
struct random_access_iterator_tag { };
所以在其余的代码中,它被用作他们所谓的tag dispatching
。
我想知道空结构是否还有其他用法。
从一个较旧的帖子我看到:
我们在 C++ 中使用空结构的三个主要原因是:
- 一个基本接口
- 模板参数
- 一种帮助重载解析的类型。 (如果我没记错的话,标签调度)
有人可以解释一下吗?
一种帮助重载解析的类型。 (如果我没记错的话,标签调度)
当你想在某个函数上使用复杂的模板特化模式时,你不要直接尝试,而是写:
template <typename T1, typename T2, other things maybe>
int foo(T1 param1, T2 param2 and so on)
{
using tag = put your complex stuff here, which produces an empty struct
detail::foo_impl(tag, std::forward<T1>(param1), std::forward<T2>(param2) and so on);
}
现在,编译器不必在模板专业化的竞争选择之间做出决定,因为使用不同的标签会得到不兼容的函数。
struct vehicle {
// common members and methods,
// including (pure) virtual ones, e.g.
virtual std::size_t num_maximum_occupants() = 0;
virtual ~vehicle() = default;
};
namespace mixins {
struct named { std::string name; };
struct wheeled { int num_wheels; public: rev() { }; };
} // namespace mixins
struct private_sedan : public vehicle, public wheeled, named {
// I dunno, put some car stuff here
//
// and also an override of `num_maximum_occupants()`
};
使基本结构完全为空可能并不常见,但如果您经常使用 mixin,这当然是可能的。 你可以检查vehicle
继承(虽然我不确定我会这样做)。
不确定这意味着什么,但大胆猜测:
template <typename T>
struct foo { };
template <typename T, typename N>
struct foo<std::array<T, N>> {
int value = 1;
};
如果您现在在函数中使用foo<T>::value
,则仅当T
为int
且很少有 (?) 异常时它才会起作用。
我也试着想出例子:
作为基础接口
// collection of very abstract vehicles
#include <vector>
struct Vehicle {};
struct Car : Vehicle {
int count_of_windows;
};
struct Bike : Vehicle {
int size_of_wheels;
};
std::vector<Vehicle> v{Bike{}, Car{}};
作为模板参数
// print same number in 3 different formats
#include <iostream>
struct dec {};
struct hex {};
struct octal {};
template<typename HOW = dec>
void print_me(int v);
template<>
void print_me<dec>(int v) {
auto f = std::cout.flags();
std::cout << std::dec << v << std::endl;
std::cout.flags(f);
}
template<>
void print_me<hex>(int v) {
auto f = std::cout.flags();
std::cout << std::hex << v << std::endl;
std::cout.flags( f );
}
template<>
void print_me<octal>(int v) {
auto f = std::cout.flags();
std::cout << std::oct << v << std::endl;
std::cout.flags(f);
}
int main() {
print_me(100);
print_me<hex>(100);
print_me<octal>(100);
}
一种帮助重载解析的类型
// add a "noexcept" qualifier to overloaded function
// the noexcept version typically uses different functions
// and a custom "abort" handler
#include <iostream>
struct disable_exceptions {};
void is_number_1() {
int v;
std::cin >> v;
if (v != 1) {
throw new std::runtime_error("AAAA");
}
}
void is_number_1(disable_exceptions) noexcept {
int v;
// use C function - they don't throw
if (std::scanf("%d", &v) != 1) {
std::abort();
}
if (v != 1) {
std::abort();
}
}
int main() {
is_number_1();
is_number_1(disable_exceptions());
}
关于“标签调度”的例子可以在cppreference iterator_tags上找到。 iterator_category()
成员用于选择不同的重载。 这样你就可以写一个不同的算法,例如迭代器是 forward_iterator,你只能前进,或者它是一个 bidirectional_iterator,你的算法可能会改变,因为你可能会走回去。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.