繁体   English   中英

C++中空结构体的使用

[英]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 ,则仅当Tint且很少有 (?) 异常时它才会起作用。

我也试着想出例子:

作为基础接口

// 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM