C++“准”标准库Boost学习指南(7):Boost.Variant

从‘准标准库’到现代C++项目:如何优雅地在VSCode中集成并使用Boost 1.84.0 本文详细介绍了如何在VSCode中优雅地集成并使用Boost 1.84.0,涵盖从编译参数优化到智能感知调校的全流程。通过现代CMake配置和VSCode环境深度调优,开发者可以高效利用Boost这一‘准标准库’,提升C++开发体验。 阅读详情
Boost.Variant

Variant库包含一个不同于union的泛型类,用于在存储和操作来自于不同类型的对象。这个库的一个特点是支持类型安全的访问,减少了不同数据类型的类型转换代码的共同问题。

Variant 库如何改进你的程序?
  • 对用户指定的多种类型的进行类型安全的存储和取回
  • 在标准库容器中存储不同类型的方法
  • 变量访问的编译期检查
  • 高效的、基于栈的变量存储

Variant 库关注的是对一组限定类型的类型安全存储及取回,即非无类的联合。Boost.Variant 库与 Boost.Any 有许多共同之外,但在功能上也有不同的考虑。在每天的编程中通常都会需要用到非无类的联合(不同的类型)。保持类型安全的一个典型方法是使用抽象基类,但这不总是可以做到的;即使可以做得,堆分配和虚拟函数的代价也可能太高。你也可以尝试用不安全的无类类型,如 void* (它会导致不幸),或者是类型安全得无限制的可变类型,如 Boost.Any. 这里我们将看到 Boost.Variant,它支持限定的可变类型,即元素来自于一组支持的类型。

许多其它的编程语言支持可变类型,它们也再次被证实是值得的。在C++内建的对可变类型的支持非常有限,只有某种形式的联合(union),而且主要是为了与C兼容而保留。Boost.Variant 通过一个类型模板 variant 补救了这种情形,并附随有安全的存储及取回值的工具。一个可变数据类型提供一个与当前值的类型无关的接口。如果你曾经用过别的可变类型,可能是仅能支持固定的一组类型。这个库不是这样的;你在使用 variant 时自己定义一组允许使用的类型,而一个程序中可以包含任意个不同的 variant 实例。为了取回保存在 variant 中的值,你要么知道当前值的真实类型,要么使用已提供的类型安全的访问者(visitor)机制。访问者机制使得 Variant 非常不同于其它可变类型的库,包括 Boost.Any (它可以持有任意类型的值),从而为处理这些类型提供了一个安全而健壮的环境。C++ 的联合只对内建类型以及 POD 类型有用,但这个库提供的非无类联合可以支持所有类型。最后,效率方面也被考虑到了,这个库基于栈存储来保存它的值,从而避免了昂贵的堆分配。

Variant 如何适用于标准库?

Boost.Variant 允许在标准库容器中存储不同的类型。由于在C++或C++标准库中都没有对可变类型的真正支持,这使得 Variant 成为了标准库的一个杰出且有用的扩充。


Variant

头文件: "boost/variant.hpp"

通过单个头文件就包含了所有 Variant 库。

"boost/variant/variant_fwd.hpp"
包含了 variant 类模板的前向声明。

"boost/variant/variant.hpp"
包含了 variant 类模板的定义。

"boost/variant/apply_visitor.hpp"
包含了对 variant 应用访问者机制的功能。

"boost/variant/get.hpp"
包含了模板函数 get.

"boost/variant/bad_visit.hpp"
包含了异常类 bad_visit 的定义。

"boost/variant/static_visitor.hpp"
包含了 visitor 类模板的定义。

以下部分摘要包含了 variant 类模板中最重要的成员。其它功能,如访问者机制,类型安全的直接取回,还有更先进的特性,如通过类型列表创建类型组等等,在 用法部分 讨论。

namespace boost {
  template <typename T1,typename T2=unspecified, ...,
    typename TN=unspecified>
  class variant {
  public:

    variant();
    
    variant(const variant& other);
    
    template <typename T> variant(const T& operand);
          
    template <typename U1, typename U2, ..., typename UN>
      variant(const variant<U1, U2, ..., UN>& operand);
    
    ~variant();

    template <typename T> variant& operator=(const T& rhs);

    int which() const;
    bool empty() const;
    const std::type_info& type() const;

    bool operator==(const variant& rhs) const;
    bool operator<(const variant& rhs) const;
  };
}


成员函数
  1. variant();
这个构造函数对 variant 的类型组中的第一个类型进行缺省构造。这意味着在声明 variant 类型时,第一个类型必须是可以被缺省构造的,或者 variant 类型本身不能被缺省构造。该构造函数传播任何从第一个类型的构造函数抛出的异常。
  1. variant(const variant& other);
这个复制构造函数复制 other 的当前值,并传播任何从 other 的当前类型的复制构造函数抛出的异常。
  1. template <typename T> variant(const T& operand);
这个构造函数从 operand 构造一个新的 variant 。operand 的类型 T, 必须可以转换为限定类型组中的某个类型。复制或转换 operand 时抛出的异常将被传播。
  1. template <typename U1,typename U2,...,typename UN>
  2.   variant(const variant<U1,U2,...,UN>& operand);
这个构造函数允许从另一个 variant 类型进行构造,后者的类型组为 U1, U2…UN, 它们必须可以转换为 T1,T2…TN (被构造的 variant 的类型组)。复制或转换 operand 时抛出的异常将被传播。
  1. ~variant();
销毁 variant, 并调用当前值的析构函数。注意,对于指针类型,不调用析构函数(销毁指针是无操作的)。析构函数不抛出异常。
  1. template <typename T> variant& operator=(const T& rhs);
这个操作符放弃当前值,并赋予值 rhs. 类型 T 必须可以转换为 variant 的限定类型组中的某个类型。如果 T 正好是 variant 当前值的类型,rhs 被复制赋值给当前值;从 T 的赋值操作符抛出的异常将被传播。如果 variant 当前值的类型不是 T, 则当前值被替换为从类型 T 的(复制)构造函数所创建的值。从构造函数抛出的异常将被传播。这个函数还可能抛出 bad_alloc.
  1. int which() const;
返回一个从零起计的索引,表示当前值类型在限定类型组中的位置。这个函数不会抛出异常。
  1. bool empty() const;
这个函数永远返回 false, 因为一个 variant 永远不会为空。这个函数的存在是为了允许泛型代码把 variant 和 boost::any 视为同一种类型来处理。这个函数不会抛出异常。
  1. const std::type_info& type() const;
返回当前值的 type_info 。这个函数不会抛出异常。
  1. bool operator==(const variant& rhs) const;
如果 *this and rhs 相等则返回 true ,即 which()==rhs.which() 且 *this 的当前值与 rhs 根据当前值的类型的相等操作是相等的。这要求限定类型组中的所有类型都必须是可以进行等同性比较的(EqualityComparable)。当前值的类型的 operator== 抛出的任何异常将被传播。
  1. bool operator<(const variant& rhs) const;
小于比较返回 which()<rhs.which() 或者,如果该索引相等,则返回对 *this 的当前值与 rhs 调用 operator< 所返回的结果。当前值的类型的 operator< 抛出的任何异常将被传播。


Boost.Variant 用法

在你的程序中使用 variant,要包含头文件 "boost/variant.hpp"。这个头文件包含了整个库,所以你不必知道要使用哪些单独的特性;以后,如果你要降低相关性,可以只包含那些解决问题所要的头文件。声明一个 variant 类型时,我们必须定义一组它可以存储的类型。最常用的办法是使用模板参数。一个可以持有类型为 int, std::string, 或 double 的值的 variant 声明如下。

  1. boost::variant<int,std::string,double> my_first_variant;
当变量 my_first_variant 被创建时,它含有一个缺省构造的 int, 因为 int 是这个 variant 可以持有的类型中的第一种类型。我们也可以传递一个可以转换为可用类型之一的值来初始化 variant.
  1. boost::variant<int,std::string,double>
  2. my_first_variant("Hello world");
我们可以随时赋给新的值,只要这个新值有确定的类型并且可以转换为 variant 可以持有的类型中的某一种,它可以很好地工作。

my_first_variant=24;
my_first_variant=2.52;
my_first_variant="Fabulous!";
my_first_variant=0;


在第一个赋值后,所含值的类型为 int; 第二个赋值后,类型为 double; 第三个后,类型为 std::string; 最后,又变回 int. 如果我们想看看,我们可以用函数 boost::get 取出这个值,如下:
  1. assert(boost::get<int>(my_first_variant)==0);
注意,如果调用 get 失败(当 my_first_variant 所含值不是类型 int 时就会发生),会抛出一个类型为 boost::bad_get 的异常。为了避免在失败时得到一个异常,我们可以传给 get 一个 variant 指针,这样 get 将返回一个指向它所含值的指针,或者如果给定类型与 variant 的值的类型不符则返回空指针。以下是它的用法:
  1. int* val=boost::get<int>(&my_first_variant);
  2. assert(val && (*val)==0);
函数 get 是访问所含值的一种直接方法,事实上它与 boost::any 的 any_cast 很相似。注意,类型必须完全符合,包括相同的 cv-限定符(const 和 volatile)。但是,可以使用限制更多的 cv-限定符。如果类型不匹配且传给 get 的是一个 variant 指针,将返回空指针。否则,抛出一个类型为 bad_get 的异常。
  1. const int& i=boost::get<const int>(my_first_variant);
过分依赖于 get 的代码很容易变得脆弱;如果我们不知道所含值的类型,我们可能会想测试所有可能的组合,就如下面这个例子的做法。

#include <iostream>
#include <string>
#include "boost/variant.hpp"

template <typename V> void print(V& v) {
  if (int* pi=boost::get<int>(&v))
    std::cout << "It's an int: " << *pi << '\n';
  else if (std::string* ps=boost::get<std::string>(&v))
    std::cout << "It's a std::string: " << *ps << '\n';
  else if (double* pd=boost::get<double>(&v))
    std::cout << "It's a double: " << *pd << '\n';

  std::cout << "My work here is done!\n";
}

int main() {
  boost::variant<int,std::string,double>
    my_first_variant("Hello there!");
  print(my_first_variant);
  my_first_variant=12;
  print(my_first_variant);
  my_first_variant=1.1;
  print(my_first_variant);
}


函数 print 现在可以正确工作,但如果我们决定改变 variant 的类型组的话会怎样?我们将引入一个微妙的bug,而不能在编译期捉住它;函数 print 不能打印任何其它我们没有预先想到的类型的值。如果我们没有使用模板函数,而是要求一个明确的 variant 类型,我们就要为不同类型的 variant 重载多个相同功能的函数。下一节将讨论访问 variant 的概念,以及这种(类型安全的)访问机制解决的问题。

访问Variants

让我们从一个例子开始,它解释了为什么使用 get 并没有你想要的那么可靠。从前面父子的代码开始,我们来修改一下 variant 可以包含的类型,并对 variant 的一个 char 值来调用 print 。

int main() {
  boost::variant<int,std::string,double,char>
    my_first_variant("Hello there!");
 
  print(my_first_variant);
  my_first_variant=12;
  print(my_first_variant);
  my_first_variant=1.1;
  print(my_first_variant);
  my_first_variant='a';
  print(my_first_variant);
}


虽然我们给 variant 的类型组增加了 char ,并且程序的最后两行设置了一个 char 值并调用 print ,编译器也不会有意见 (注意,print 是以 variant 的类型来特化的,所以它可以很容易适应新的 variant 定义)。以下是这个程序的运行结果:
It's a std::string: Hello there!
My work here is done!
It's an int: 12
My work here is done!
It's a double: 1.1
My work here is done!
My work here is done!

这个输出显示了一个问题。最后一个"My work here is done!"之前没有值的报告。原因是很简单,print 不能输出除了它原来设计好的那些类型(std::string, int, 和 double)以外的任何值,但它可以干净地编译和运行。如果 variant 的当前类型不被 print 支持,它的值就会被简单地忽略掉。使用 get 还有更多潜在的问题,例如 if 语句的顺序要与类的层次相一致。注意,这并不是说你应该完全避免使用 get;它只是说有些时候它不是最好的方法。有一种更好的机制,可以允许我们规定哪些类型的值可以接受,并且这些规定是在编译期生效的。这是就 variant 访问机制的作用。通过把一个访问器应用到 variant,编译器可以保证它们完全兼容。Boost.Variant 中这些访问器是带有一些函数调用操作符的函数对象,这些函数调用操作符接受与它们所访问的 variant 可以包含的类型组相对应的参数。
现在我们用访问器来重写那个声名狼籍的函数 print ,如下:

class print_visitor : public boost::static_visitor<void> {
public:
  void operator()(int i) const {
    std::cout << "It's an int: " << i << '\n';
  }

  void operator()(std::string s) const {
    std::cout << "It's a std::string: " << s << '\n';
  }

  void operator()(double d) const {
    std::cout << "It's a double: " << d << '\n';
  }

};


要让 print_visitor 成为 variant 的一个访问器,我们要让它派生自 boost::static_visitor 以获得正确的 typedef (result_type), 并明确地声明这个类是一个访问器类型。这个类实现了三个重载版本的函数调用操作符,分别接受一个 int, 一个 std::string, 和一个 double 。为了访问 variant, 你要用函数 boost::apply_visitor(visitor, variant). 如果我们用对 apply_visitor的调用来替换前面的 print 调用,我们可以得到如下代码:

int main() {
  boost::variant<int,std::string,double,char>
    my_first_variant("Hello there!");
 
  print_visitor v;
 
  boost::apply_visitor(v,my_first_variant);
  my_first_variant=12;
  boost::apply_visitor(v,my_first_variant);
  my_first_variant=1.1;
  boost::apply_visitor(v,my_first_variant);
  my_first_variant='a';
  boost::apply_visitor(v,my_first_variant);
}


这里,我们创建了一个 print_visitor, 名为 v, 并把它应用于赋值后的 my_first_ variant 。因为我们没有一个函数调用操作符接受 char, 这段代码会编译失败,是吗?错!一个 char 可以转换为一个 int, 所以这个访问器可以兼容我们的 variant 类型。以下是程序运行的结果。
It's a std::string: Hello there!
It's an int: 12
It's a double: 1.1
It's an int: 97

这里我们可以学到两件事情:第一个是字母 a 的 ASCII 码值为 97, 更重要的是第二个,如果一个访问器以传值的方式传递参数,则传送的值可以应用隐式转换。如果我们想访问器只能使用精确的类型(同时也避免拷贝从 variant 得到的值),我们必须修改访问器的调用操作符传递参数的方式。以下这个版本的 print_visitor 只能使用类型 int, std::string, 和 double; 以及可以隐式转换到这些类型的引用的其它类型。

class print_visitor : public boost::static_visitor<void> {
public:
  void operator()(int& i) const {
    std::cout << "It's an int: " << i << '\n';
  }

  void operator()(std::string& s) const {
    std::cout << "It's a std::string: " << s << '\n';
  }

  void operator()(double& d) const {
    std::cout << "It's a double: " << d << '\n';
  }
};


如果再编译一下这个程序,编译器就不高兴了,它会输出如下信息:
c:/boost_cvs/boost/boost/variant/variant.hpp:
In member function `typename Visitor::result_type boost::detail:: variant::
invoke_visitor<Visitor>::internal_visit(T&, int)
[with T = char, Visitor = print_visitor]':

[Snipped lines of irrelevant information here]

c:/boost_cvs/boost/boost/variant/variant.hpp:807:
error: no match for call to `(print_visitor) (char&)'
variant_sample1.cpp:40: error: candidates are:
void print_visitor::operator()(int&) const
variant_sample1.cpp:44: error:
void print_visitor::operator()(std::string&) const
variant_sample1.cpp:48: error:
void print_visitor::operator()(double&) const

这个错误指出了问题:没有一个候选的函数接受 char 参数!为什么说类型安全的编译期访问机制是一个强大的机制,这正是一个重要的原因。它使得访问机制强烈依赖于类型,避免了讨厌的类型变换。创建访问器与创建其它函数对象一样容易,因此学习曲线并不陡峭。当 variant 中的类型组可能会改变时(它们总是倾向于变化!),创建访问器要比单单依赖 get 更可靠。虽然开始需要更高的代价,但绝对是值得的。

泛型访问器

通过使用访问器机制和泛型的调用操作符,可以创建能够接受任意类型的泛型访问器(无论是在语法上还是语义上,都可以实现泛型调用操作符)。这对于统一地处理不同的类型非常有用。C++的操作符就是"通用"性的一个典型例子,如算术和IO流的位移操作符。以下例子使用 operator<< 来输出 variant 的值到一个流。

#include <iostream>
#include <sstream>
#include <string>
#include <sstream>
#include "boost/variant.hpp"

class stream_output_visitor :
  public boost::static_visitor<void> {
  std::ostream& os_;
public:
  stream_output_visitor(std::ostream& os) : os_(os) {}

  template <typename T> void operator()(T& t) const {
    os_ << t << '\n';
  }
};

int main() {
  boost::variant<int,std::string> var;
  var=100;
  boost::apply_visitor(stream_output_visitor(std::cout),var);
  var="One hundred";
  boost::apply_visitor(stream_output_visitor(std::cout),var);
}


主要思想是 stream_output_visitor 中的调用操作符是一个成员函数模板,它在访问每一种类型(本例中是 int 和 std::string)时分别实例化。因为 std::cout << 100 和 std::cout << std::string("One hundred") 都已经有定义了,所以这段代码可以编译并工作良好。

当然,操作符仅是可以使用泛型访问器的一个例子;它们常常应用于更多的类型。在某些值上调用函数,或者将它们作为参数传给其它的函数时,要求就是对于所有传给操作符的类型都要有相应的成员函数存在,并且对于被调用的函数要有合适的重载。这种泛型调用操作符的另一个有趣的方面是,可以对某些类型特化其行为,但对于其余类型则仍允许泛型的实现。在某种意义上,这涉及到模板特化,即基于类型信息的行为特殊化。

二元访问器

我们前面看到的访问器都是一元的,即它们只接受一个 variant 作为唯一的参数。二元访问器接受两个(可能是不同的) variant. 这种概念对于实现两个 variant 间的关系很有用。作为例子,我们为 variant 类型将创建一个按字典顺序的排序。为此,我们使用一个来自于标准库的非常有用的组件:std::ostringstream. 它接受任意可流输出的东西,并且在需要时产生一个独立的 std::string 。我们从而可以按字典序比较完全不同的 variant 类型,只要假设所有限定的类型都支持流输出。和普通的访问器一样,二元访问器也派生自 boost::static_visitor, 并且用模板参数表示调用操作符的返回类型。因为我们是创建一个谓词,因此返回类型为 bool. 以下是一个我们即将用到的二元谓词。

class lexicographical_visitor :
  public boost::static_visitor<bool> {
public:
  template <typename LHS,typename RHS>
  bool operator()(const LHS& lhs,const RHS& rhs) const {
    return get_string(lhs)<get_string(rhs);
  }
private:
  template <typename T> static std::string
  get_string(const T& t) {
    std::ostringstream s;
    s << t;
    return s.str();
  }

  static const std::string& get_string(const std::string& s) {
    return s;
  }
};


这里的调用操作符泛化了它的两个参数,这意味着它接受任意两种类型的组合。对于 variant 的可用类型组的要求就是它们必须是可流输出(OutputStreamable)的。成员函数模板 get_string 使用一个 std::ostringstream 来把它的参数转换为字符串表示,所以要求参数必须是可流输出的(为了使用 std::ostringstream, 记得要包含头文件 <sstream>)。成员函数 get_string 针对类型为 std::string 的参数进行特化,由于类型已经符合要求,所以它跳过了 std::ostringstream 而直接返回它的参数。在两个参数都转为 std::string 以后,剩下的就是使用 operator< 来比较它们了。现在我们把这个访问器放入测试代码,来对一个容器中的元素进行排序(我们还将重用我们在本章前面创建的 stream_output_visitor )。

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include "boost/variant.hpp"

int main() {
  boost::variant<int,std::string> var1="100";
  boost::variant<double> var2=99.99;

  std::cout << "var1<var2: " <<
    boost::apply_visitor(
      lexicographical_visitor(),var1,var2) << '\n';

  typedef std::vector<
    boost::variant<int,std::string,double> > vec_type;

  vec_type vec;
  vec.push_back("Hello");
  vec.push_back(12);
  vec.push_back(1.12);
  vec.push_back("0");

  stream_output_visitor sv(std::cout);
  std::for_each(vec.begin(),vec.end(),sv);

  lexicographical_visitor lv;
  std::sort(vec.begin(),vec.end(),boost::apply_visitor(lv));

  std::cout << '\n';
  std::for_each(vec.begin(),vec.end(),sv);
};


首先,我们将访问应用于两个 variants, var1 和 var2, 如下:
  1. boost::apply_visitor(lexicographical_visitor(),var1,var2)
如你所见,与一元访问器不同的是,有两个 variant 被传递给函数 apply_visitor. 一个更为常见的用例是使用这个谓词来对元素进行排序,我们这样来做:
  1. lexicographical_visitor lv;
  2. std::sort(vec.begin(),vec.end(),boost::apply_visitor(lv));
当 sort 算法被执行时,它使用我们传入的谓词来比较它的元素,它是一个 lexicographical_visitor 实例。注意,boost::variant 已经定义了 operator<, 所以不使用谓词也可以对容器进行排序。
  1. std::sort(vec.begin(),vec.end());
但是这种缺省的排序是首先使用 which 来检查当前值的索引,所以元素的排列顺序将是 12, 0, Hello, 1.12, 而我们想要的是按字典序来排序。因为 variant 类已经提供了 operator< 和 operator== ,所以 variant 可以用作所有标准库容器的元素类型。当缺省的关系比较不够用时,你需要用二元访问器来实现一个。

更多应该知道的事情

我们并没有涉及到 Boost.Variant 库的所有功能。其它更为先进的特性不如我们已经提到的那么常用。但是,我会简要地说一下,因此你将至少知道在需要时可以找到哪些可用的东西。宏 BOOST_VARIANT_ENUM_PARAMS, 可用于为 variant 类型重载/特化函数和类模板。这个宏用于列举 variant 可以包含的类型组。还有支持使用类型序列来创建 variant 类型,即通过 make_variant_over 编译期列表来表示 variant 的类型组。还有递归的 variant 类型,可用于创建它们自己类型的表达式,递归 variant 类型使用 recursive_wrapper, make_recursive_variant, 和 make_recursive_variant_over. 如果你需要这些额外的特性,在线文档可以很好地解释它们。


Variant 总结

类别联合(discriminated unions)在日常编程中非常有用,这个事实无须惊讶,Boost.Variant 库提供了高效且易用的 variant 类型,它正是基于类别联合的。因为C++的联合对于很多类型很难使用(它只支持内建类型和 POD 类型),长期以来一直需要别的东西来取代它。许多创建类别联合的尝试都存在某些重要的缺点。例如,早期的尝试通常仅支持固定的一组类型,的确妨碍了维护性和灵活性。Boost.Variant 通过模板避免了这些限制,理论上允许创建任意的 variant 类型。在处理类别联合时类型转换代码总会成为问题所在;在处理前需要测试当前值的类型,这导致了维护的麻烦。Boost.Variant 提供了简单的值取回操作以及类型安全的访问机制,这是解决问题的新颖方法。最后,效率也是早期的尝试所关心的,这个库也很好地照顾到了效率,它使用基于栈的存储,而不是基于堆的。

Boost.Variant 是一个成熟的库,有非常多的特性,使用 variant 类型容易且高效。这是 Boost.Any 库的补充,同样应该成为你的专业C++工具箱中的一员。

Boost.Variant 的作者是 Eric Friedman 和 Itay Maman.

解决Visual Studio中Boost库链接错误:无法解析的外部符号boost::throw_exception 在C++开发中,链接器(Linker)负责将编译后的目标文件与库文件组合成可执行程序,其核心原理是解析和匹配符号定义。当符号未定义或冲突时,会引发“无法解析的外部符号”错误,直接影响项目的构建与运行。这一问题的技术价值在于,它揭示了开发环境配置一致性对项目稳定性的关键作用,尤其是在处理第三方库时。在Windows平台的Visual Studio开发环境中,使用Boost库时经常遇到此类问题,典型场景包括跨模块链接、多配置(Debug/Release)构建以及团队协作开发。本文聚焦于Boost库中常见的`bo 阅读详情

相关推荐

Boost库深度解析:现代C++开发中不可替代的高级工具与实战指南

在C++开发中,异步编程和高效数据管理是构建高性能系统的核心挑战。异步I/O模型通过事件驱动机制,允许单线程处理大量并发连接,显著提升服务器程序的吞吐量和可扩展性。其技术价值在于能够充分利用现代硬件资源,避免线程阻塞带来的性能损耗。这一原理在高并发网络服务器、实时通信后端等场景中尤为重要。Boost.Asio库作为异步网络编程的基石,提供了跨平台的前摄器模式实现,将复杂的底层系统调用封装为统一的C++接口,并深度集成了现代C++特性如lambda表达式和移动语义。同时,在数据检索方面,传统容器往往难以满足多

weixin_34226182的博客 313

C++“准”标准库Boost学习指南-variant的使用

Variant库包含一个不同于union的泛型类,用于在存储和操作来自于不同类型的对象。这个库的一个特点是支持类型安全的访问,减少了不同数据类型的类型转换代码的共同问题。 Variant 库如何改进你的程序? 对用户指定的多种类型的进行类型安全的存储和取回在标准库容器中存储不同类型的方法变量访问的编译期检查高效的、基于栈的变量存储 Variant 库关注的是对一组限定类型

a418382926的专栏 8748

C++17核心特性解析:从std::optional到结构化绑定的现代C++实践

在C++编程语言的发展历程中,现代C++标准通过引入更安全、更高效的语言特性和标准库组件,持续提升开发者的生产力和代码质量。其核心原理在于通过类型系统和编译期计算,在保持零开销抽象的同时,提供更强的表达能力和运行时安全性。这些改进的技术价值在于显著减少了常见编程错误,如空指针解引用和类型不安全操作,并简化了泛型编程的复杂性。在实际应用场景中,例如配置解析、消息传递系统和文件操作等,现代特性能够直接替代传统的、易出错的模式。本文聚焦于C++17标准,深入探讨了`std::optional`、`std::var

weixin_34005042的博客 577

boost::variant的简单介绍

boost::variant简介 boost::Variant 是定义在boost/variant.hpp中的模板类,它的功能类似与union。Variant是一个模板,所以在使用时必须传入至少一个类型参数。Variant实例所存储的数据类型只能是传入类型中的一种。 例如:boost::Variant<double, int, string> variant; variant = "he

litianze99的专栏 3380

C++17核心特性解析:结构化绑定、optional与编译期编程实战

C++作为高性能系统编程语言,其标准演进始终围绕提升开发效率与代码安全性展开。从C++11的自动类型推导到C++17的工程化增强,现代C++致力于通过类型系统与编译期计算减少运行时错误。其核心原理在于利用静态检查与零开销抽象,在保持底层控制力的同时提供高级表达方式。技术价值体现在降低维护成本、提升团队协作效率,并能从根源消除数据竞争等隐晦Bug。应用场景广泛覆盖网络服务、基础设施库、游戏引擎及嵌入式系统,其中C++17的结构化绑定、std::optional等特性直接针对多返回值处理、空值表示等工程痛点。本

weixin_33747129的博客 457

现代C++实战指南:从RAII到协程,掌握核心范式与工程实践

C++作为一门高性能系统编程语言,其核心价值在于对硬件资源的精细控制和零开销抽象。通过RAII(Resource Acquisition Is Initialization)机制,现代C++实现了资源管理的自动化,将资源生命周期与对象绑定,从根本上解决了内存泄漏和异常安全问题。这一原理催生了智能指针、移动语义等关键技术,使得开发者能够编写出既安全又高效的代码。在工程实践中,这些特性与标准库组件(如容器、算法、并发工具)结合,广泛应用于高性能计算、游戏引擎、嵌入式系统等场景。本文聚焦于现代C++的实战应用,深

weixin_34295316的博客 379

boost::variant类的使用

C++“准”标准库Boost学习指南-variant的使用。 boost I 容器与数据结构。

danshiming的博客 333

现代C++演进:从C++11到C++23的核心特性与实战指南

C++作为一门高性能系统编程语言,其核心价值在于提供零成本抽象和精细的资源控制能力。其设计哲学围绕RAII(资源获取即初始化)展开,通过将资源生命周期与对象绑定,从根本上解决了手动内存管理带来的复杂性和安全隐患。这一原理催生了智能指针、移动语义等关键技术,极大地提升了代码的安全性和执行效率。随着多核处理器普及,C++标准库也引入了原生并发支持,包括线程、互斥量和内存模型,为构建高性能并发程序提供了坚实基础。这些技术广泛应用于游戏引擎、高频交易系统、嵌入式设备及大型基础设施软件等场景。本文聚焦于C++11引入

keke1314520的博客 324

boost总结之variant

boost 是 C++“准”标准库(在低版本的标准库中没有,只能额外安装boost库,在较新的c++版本标准库已经加入了boost)。 下面主要学习boost库中variant的用法: boost的variant库类似于联合体,但是联合体中只能接受POD类型,但variant中并无此限制,它可以接受任意的类型.   boost::variant int, std::st

wonengguwozai的博客 1114

【BOOST C++Data-struct专题04】【04】Boost.Variant

Boost.Variant 提供了一个类似于 union 的名为 boost::variant 的类。您可以将不同类型的值存储在 boost::variant 变量中。在任何时候只能存储一个值。分配新值时,旧值将被覆盖。但是,新值的类型可能与旧值不同。唯一的要求是这些类型必须作为模板参数传递给 boost::variant,这样它们才能为 boost::variant 变量所知。

gongdiwudu的专栏 4865

boost::variant2模块转换构造抛出相关的测试程序

boost::variant2模块转换构造抛出相关的测试程序实现功能C++实现代码 实现功能 boost::variant2模块转换构造抛出相关的测试程序 C++实现代码 #if defined(_MSC_VER) # pragma warning( disable: 4702 ) // unreachable code #endif #include <boost/variant2/variant.hpp> #include <boost/core/lightweight_test.hp

希望我的博客,能帮上你解决学习中工作中所遇到的问题 612

Boost库编译参数详解:从b2命令到生成你想要的lib/dll文件

本文详细解析了Boost库编译参数,从b2命令到生成定制化lib/dll文件的完整过程。涵盖了编译器选择、链接方式、运行时库配置等核心参数,帮助开发者精准控制Boost库的生成特性,适用于VS、vscode等多种开发环境。通过实战案例和高级技巧,提升Boost库的编译效率和使用体验。

ahhli4007的博客 427

浅析C++中boost.variant的几种访问方法

Boost.Variant Variant库包含一个不同于union的泛型类,用于在存储和操作来自于不同类型的对象。这个库的一个特点是支持类型安全的访问,减少了不同数据类型的类型转换代码的共同问题。 Variant 库如何改进你的程序?      •对用户指定的多种类型的进行类型安全的存储和取回      •在标准库容器中存储不同类型的方法      •变量访问的编译期检查      •高效的、基于栈的变量存储 Variant 库关注的是对一组限定类型的类型安全存储及取回,即非无类的联合。Boost.Variant 库与 Boost.Any 有许多共同之外,但在功能上也有不同的考虑。在每天的

Boost 第七章 容器与数据结构

本文章所有内容源于《BOOST程序库完全开发指南:深入C++“准”标准库(第3版)》第七章 1. array 1.1 功能: 数组 1.2 头文件: #include <boost/array.hpp> using namespace boost; 1.3 用法: using namespace std; #include <boost/array.hpp> using namespace boost; #include <iostream> /////////////

weixin_42325783的博客 352

GNSS接收机性能测试指标详解:TTFF、位置精度、灵敏度等关键参数如何评估

本文详细解析了GNSS接收机性能测试的关键指标,包括首次定位时间(TTFF)、位置精度、灵敏度等,帮助工程师系统评估接收机性能。通过多频段协同定位和载波相位测试等技术,提升定位精度至亚米级。同时,介绍了灵敏度测试的极限挑战和授时精度的专业测试方案,为GNSS接收机的性能优化提供实用指导。

cuda7parallel的博客 961

二维卡尔曼滤波位置,速度融合

这里写自定义目录标题欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注脚注释也是必不可少的KaTeX数学公式新的甘特图功能,丰富你的文章UML 图表FLowchart流程图导出与导入导出导入 欢迎使用Ma...

Johnlee2019的博客 8451

Boost库编译与CMake集成终极指南:从B2到现代C++项目实践

C++库的构建与集成是项目开发中的基础环节,其核心在于理解构建系统的原理与依赖管理机制。构建系统如CMake和B2(Boost.Build)通过抽象平台差异,实现了跨平台编译自动化,其技术价值在于提升代码复用性、确保环境一致性。在实际应用场景中,大型C++项目常依赖Boost这类高质量基础库,但Boost库的编译选项复杂,涉及静态链接、动态链接、多线程支持等关键配置。本文聚焦于Boost库的完整编译流程,深入解析B2构建系统的variant、link等核心参数,并详细阐述如何在CMake项目中通过find_

weixin_33801856的博客 315

STM32G474片内运放实战:从独立模式到PGA与ADC采样链路

在嵌入式控制系统中,运放是连接模拟信号与数字处理的关键环节。传统方案依赖外部分立运放搭建放大电路,存在成本高、PCB布局敏感、一致性差等痛点。STM32G474在MCU内部集成多路可编程运放(OPAMP),支持独立模式、跟随器、PGA以及比较器模式,配合内部ADC和DMA可构成完整模拟采集链路。通过片内互联,信号路径大幅缩短,抗干扰性和通道一致性显著提升,尤其适用于电机电流采样、数字电源等场景。本文以STM32G474为例,详解从独立模式到PGA增益配置,再到ADC与DMA联动调试的完整过程,并给出采样时间

weixin_29194255的博客 246

ubuntu下编译安装boost库

环境:ubuntu 12.04 32bit,boost 1.49 前期准备:boost中,用到了别的函数库,所以为了使用boost中相应的功能,需要先安装系统中可能缺失的库  apt-get install mpi-default-dev  #安装mpi库 apt-get install libicu-dev     #支持正则表达式的UNICODE字符集  ...

weixin_30951389的博客 227

告别虚拟机!用ZYNQ7000和PYNQ 2.6.0打造一个能实时识别人脸的“智能摄像头”

本文详细介绍了如何利用Xilinx ZYNQ7000开发板和PYNQ 2.6.0框架构建实时人脸识别系统。通过硬件加速和嵌入式开发技术,该系统实现了低延迟、低功耗的独立运行能力,适用于边缘计算和物联网应用。文章包含从环境搭建到核心功能实现的完整指南,并提供了性能优化和问题排查的实用技巧。

weixin_42543046的博客 390

【LE Audio】CAP精讲[1]: 从理论到实操,CAP 协同流程入门全攻略

LEAudio生态中的CommonAudioProfile(CAP)是一个关键协同框架,它通过整合基础音频协议(BAP/VCP/MICP等)构建多设备协同能力。CAP定义了三大核心功能:音频流管理、设备组控制和统一交互服务,解决了传统蓝牙音频在多设备场景下的协同痛点。其规范历经v1.0到v1.0.1的优化,修正了角色定义、连接流程等关键模块,提升设备兼容性。CAP采用严格的语言约定(shall/must/should)和术语体系(ContextType/CCID/协同集),确保不同厂商设备能相互理解。

byte轻骑兵的技术小窝 2万+

C++物联网网关交互协议设计:从ctGateway看异步IO与插件化架构

物联网网关作为连接物理设备与云平台的核心枢纽,其交互协议层是实现稳定、高效通信的关键。协议层本质上是一个通信框架,负责异构协议的翻译、连接管理、数据路由与安全传输。在资源受限的边缘计算场景中,C++凭借其零成本抽象和高效内存管理能力,成为构建高性能网关的首选。通过异步IO模型(如Reactor/Proactor模式)处理高并发连接,并结合插件化架构灵活适配Modbus、MQTT等多样协议,能够确保数据在复杂网络环境中准确、安全地上传下达。本文以开源项目ctGateway为例,深入剖析了其基于C++的协议栈分

weixin_33694172的博客 549

xtop:multi_driven_net与incomplete_timing_cell fail reason 分析

man multi_driven_net”可以得知,这个问题是multi driven结构引起的,使用命令“get_failed_pins -reason multi_driven_net”,获取violation pin。“man incomplete_timing_cell”可以得知,这个fail reason的原因是缺少timing library cell,检查并补全后这个错误就显示了。这里选择的解决方法是修改lib文件,把inout改成input,重新lib2idb。通过这种方法解决了问题。

m0_61544122的博客 564
上一篇: C++“准”标准库Boost学习指南(6):Boost.Any
下一篇: C++“准”标准库Boost学习指南(8):Boost.Tuple
cbNotes
博客等级 码龄14年 295粉丝 57原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值