概述
描述
此库实现了一种类型安全的判别/标记联合类型,variant<T…>
,它与 C++17 标准的 std::variant<T…>
在 API 上兼容。
一个 variant<T1, T2, …, Tn>
变量可以保存类型 T1
、T2
、…、Tn
中的任何值。 例如,variant<int64_t, double, std::string>
可以保存一个 int64_t
值、一个 double
值或一个 string
值。
这种类型有时被称为“标记联合”,因为它大致等同于
struct V
{
enum tag { tag_int64_t, tag_double, tag_string };
tag tag_;
union
{
int64_t i_;
double d_;
std::string s_;
};
};
使用示例
Variant 可用于表示动态类型的数值。 形式为
server.host=test.example.com
server.port=9174
cache.max_load=0.7
的配置文件可以表示为 std::map<std::string, variant<int64_t, double, std::string>>
。
Variant 也可以表示多态。 举一个经典的例子,一个多态的形状集合
#define _USE_MATH_DEFINES
#include <iostream>
#include <vector>
#include <memory>
#include <cmath>
class Shape
{
public:
virtual ~Shape() = default;
virtual double area() const = 0;
};
class Rectangle: public Shape
{
private:
double width_, height_;
public:
Rectangle( double width, double height ):
width_( width ), height_( height ) {}
virtual double area() const { return width_ * height_; }
};
class Circle: public Shape
{
private:
double radius_;
public:
explicit Circle( double radius ): radius_( radius ) {}
virtual double area() const { return M_PI * radius_ * radius_; }
};
double total_area( std::vector<std::unique_ptr<Shape>> const & v )
{
double s = 0.0;
for( auto const& p: v )
{
s += p->area();
}
return s;
}
int main()
{
std::vector<std::unique_ptr<Shape>> v;
v.push_back( std::unique_ptr<Shape>( new Circle( 1.0 ) ) );
v.push_back( std::unique_ptr<Shape>( new Rectangle( 2.0, 3.0 ) ) );
std::cout << "Total area: " << total_area( v ) << std::endl;
}
可以替换为 variant<Rectangle, Circle>
值的集合来表示。 这需要提前知道可能的 Shape
类型,通常情况如此。 作为回报,我们不再需要虚函数,也不需要使用 new Rectangle
和 new Circle
在堆上分配值。
#define _USE_MATH_DEFINES
#include <iostream>
#include <vector>
#include <cmath>
#include <boost/variant2/variant.hpp>
using namespace boost::variant2;
struct Rectangle
{
double width_, height_;
double area() const { return width_ * height_; }
};
struct Circle
{
double radius_;
double area() const { return M_PI * radius_ * radius_; }
};
double total_area( std::vector<variant<Rectangle, Circle>> const & v )
{
double s = 0.0;
for( auto const& x: v )
{
s += visit( []( auto const& y ){ return y.area(); }, x );
}
return s;
}
int main()
{
std::vector<variant<Rectangle, Circle>> v;
v.push_back( Circle{ 1.0 } );
v.push_back( Rectangle{ 2.0, 3.0 } );
std::cout << "Total area: " << total_area( v ) << std::endl;
}
构造和赋值
如果我们看一下
v.push_back( Circle{ 1.0 } );
这行代码,我们可以推断出 variant<Rectangle, Circle>
可以从 Circle
(和 Rectangle
)构造(隐式),并且确实可以。 它也可以被赋值为 Circle
或 Rectangle
。
variant<Rectangle, Circle> v = Circle{ 1.0 }; // v holds Circle
v = Rectangle{ 2.0, 3.0 }; // v now holds Rectangle
如果我们尝试从既不是 int
也不是 float
的东西(例如 (short)1
)构造 variant<int, float>
,则行为“如同” variant
声明了两个构造函数,
variant::variant(int x);
variant::variant(float x);
并使用标准重载解析规则来选择将要使用的那个。 因此,variant<int, float>((short)1)
将保存一个 int
。
检查值
将值放入 variant
很容易,但取出值必然会比较复杂。 variant<int, float>
不可能定义成员函数 get() const
,因为此类函数需要在编译时确定其返回类型,而正确的返回类型是 int
还是 float
只能在运行时才知道。
有几种方法可以解决这个问题。 首先,有访问器成员函数
std::size_t variant::index() const noexcept;
它返回当前类型的从零开始的索引。 对于 variant<int, float>
,对于 int
它将返回 0
,对于 float
它将返回 1
。
一旦我们有了索引,我们就可以使用自由函数 get<N>
来获取该值。 由于我们将类型索引传递给 get
,因此它知道返回什么。 get<0>(v)
将返回 int
,get<1>(v)
将返回 float
。
void f( variant<int, float> const& v )
{
switch( v.index() )
{
case 0:
// use get<0>(v)
break;
case 1:
// use get<1>(v)
break;
default:
assert(false); // never happens
}
}
如果我们调用 get<0>(v)
,而 v.index()
当前不是 0
,则会抛出异常(类型为 bad_variant_access
)。
另一种方法是使用 get<int>(v)
或 get<float>(v)
。 这工作方式类似。
另一种避免 bad_variant_access
的可能性方法是使用 get_if
。 它不是返回对包含值的引用,而是返回指向它的指针,返回 nullptr
以指示类型不匹配。 get_if
接受指向 variant
的指针,因此在我们的示例中,我们将使用以下内容
void f( variant<int, float> const& v )
{
if( int const * p = get_if<int>(&v) )
{
// use *p
}
else if( float const * p = get_if<float>(&v) )
{
// use *p
}
else
{
assert(false); // never happens
}
}
访问
最后但并非最不重要的是,还有 visit
。 visit(f, v)
使用 variant
v
中包含的值调用函数对象 f
并返回结果。 当 v
是 variant<int, float>
时,它将使用 int
或 float
调用 f
。 函数对象必须准备好接受两者。
在实践中,这可以通过让函数采用可以传递 int
或 float
的类型(例如 double
)来实现。
double f( double x ) { return x; }
double g( variant<int, float> const& v )
{
return visit( f, v );
}
通过使用具有重载的 operator()
的函数对象
struct F
{
void operator()(int x) const { /* use x */ }
void operator()(float x) const { /* use x */ }
};
void g( variant<int, float> const& v )
{
visit( F(), v );
}
或者通过使用多态 lambda,就像我们在 Circle
/Rectangle
示例中所做的那样
void g( variant<int, float> const& v )
{
visit( [&]( auto const& x ){ std::cout << x << std::endl; }, v );
}
visit
也可以接受多个 variant
。 visit(f, v1, v2)
调用 f(x1, x2)
,其中 x1
是 v1
中包含的值,x2
是 v2
中的值。
默认构造
variant
的默认构造函数对列表中的第一个类型进行值初始化。 variant<int, float>{}
保存 0
(类型为 int
),variant<float, int>{}
保存 0.0f
。
这通常是期望的行为。 但是,在 variant<std::mutex, std::recursive_mutex>
等情况下,人们可能合理地希望避免默认构造 std::mutex
。 提供了一种类型 monostate
,可以在这些情况下用作第一种类型。 variant<monostate, std::mutex, std::recursive_mutex>
将默认构造一个 monostate
,这基本上是一个空操作,因为 monostate
实际上是一个空的 struct
。
修订历史
1.88.0 中的变更
-
对于索引,使用最小的合适无符号类型。
1.83.0 中的变更
-
添加了
uses_double_storage()
。
1.81.0 中的变更
-
添加了对
boost::json::value_from
和boost::json::value_to
的支持。
1.79.0 中的变更
-
为
monostate
添加了operator<<
。
1.78.0 中的变更
-
添加了
<boost/variant2.hpp>
。 -
添加了
unsafe_get<I>
。 -
添加了
visit_by_index
。 -
添加了
operator<<
。
1.76.0 中的变更
-
改进了双缓冲情况下的生成代码。
1.74.0 中的变更
-
添加了对
visit
中派生类型的支持 -
提高了许多(数百个)替代方案的编译性能。
-
添加了对
visit<R>
的支持
1.73.0 中的变更
-
添加了对
std::hash
,boost::hash
的支持。 -
当
T…
中的所有类型都是 trivial 时,variant<T…>
现在是 trivial 的。 这通过使其能够传递到函数并从函数返回寄存器来提高性能。
1.71.0 中的变更
在 Boost 正式审查之后,该实现已更改为提供强异常安全保证,而不是基本保证。 expected
已被删除。
设计
特性
此 variant
实现具有两个显着特征
-
它永远不会“没有值”,也就是说,
variant<T1, T2, …, Tn>
具有一个不变性,即它始终包含类型T1
、T2
、…、Tn
之一的有效值。 -
它在赋值和
emplace
上提供强异常安全保证。
除非所有包含的类型都具有非抛出的移动构造函数,否则这是通过使用双重存储来实现的。
原理
永不为空值
variant<X, Y, Z>
只能保存 X
类型、Y
类型或 Z
类型的值,而不能保存其他值,这在直觉上是有道理的。
如果我们将 variant
视为 union
的扩展,因为 union
具有一个名为“没有活动成员”的状态,因此可以认为 variant<X, Y, Z>
也应该具有这样的附加状态,而不包含 X
、Y
、Z
中的任何一个。
然而,这使得 variant
在实践中不太方便,并且作为构建块的用处也较小。 如果我们确实需要一个只保存 X
、Y
或 Z
的变量,则额外的空状态会带来需要解决的复杂性。 并且在我们确实需要这个额外的空状态的情况下,我们可以只使用 variant<empty, X, Y, Z>
,并使用合适的 struct empty {};
。
从纯粹的设计角度来看,没有额外空状态的理由是充分的。 然而,实施方面的考虑则另有说法。
当我们用另一个(类型为 Y
)替换 variant
的当前值(例如,类型为 X
)时,由于新值需要占用与旧值相同的存储空间,我们需要首先销毁旧的 X
,然后在它的位置构造一个新的 Y
。 但由于这是 C++,因此构造可能会因异常而失败。 此时,variant
处于我们已同意它不能处于的“没有活动成员”状态。
这是一个合理的问题,正是这个问题使得拥有一个空的/无值的状态如此吸引人。 我们只需在异常时将 variant
保持为空即可。
不过,如上所述,从设计的角度来看,这是不可取的,因为它使组件的用处减少并且不够优雅。
有几种方法可以解决这个问题。 最直接的方法是禁止构造可能抛出的类型。 由于我们总是可以先创建一个临时值,然后使用移动构造函数来初始化 variant
中的值,因此只需要非抛出的移动构造函数,而不是所有构造函数都必须是非抛出的。
不幸的是,在至少一种流行的标准库实现中,基于节点的容器(如 std::list
和 std::map
)可能具有抛出移动构造函数。 禁止 variant<X, std::map<Y, Z>>
几乎是不切实际的,因此无法避免异常情况。
在发生异常时,我们还可以构造一些其他值,使 variant
有效;但在一般情况下,该构造也可能抛出。 如果其中一种类型具有非抛出的默认构造函数,我们可以使用它;但如果没有,我们就不能。
Boost.Variant 在此采取的方法是在堆上分配该值的临时副本。 在发生异常时,可以将指向该临时副本的指针存储到 variant
中。 指针操作不会抛出异常。
另一种选择是使用双缓冲。 如果我们的 variant
占用两倍的存储空间,我们可以在未使用的半部分中构造新值,然后在构造成功后,销毁另一半中的旧值。
当 std::variant
被标准化时,这些方法都被认为是不受欢迎的,因为它们要么引入开销,要么对 variant
可以包含的类型过于严格。 因此,作为一种折衷方案,std::variant
采取了一种可以(不客气地)描述为“既要又要”的方式。
由于所描述的异常情况相对罕见,std::variant
有一个特殊情况,称为“无值(valueless)”,当发生异常时会进入这种状态,但接口尽可能少地承认它的存在,允许用户假装它不存在。
从实际的角度来看,这可能不算太糟糕,但这让许多人感到不满意。 "从不"发生的罕见状态往往未经充分测试,而当 "从不" 真的发生时,通常是在最不方便的时候。
此实现不遵循 std::variant
;它静态地保证 variant
永远不会处于无值状态。 提供函数 valueless_by_exception
是为了兼容性,但它始终返回 false
。
相反,如果包含的类型使得在更改包含的值时无法避免异常情况,则使用双重存储。
强异常安全性
最初的提交仅提供了基本的异常安全保证。 如果尝试通过赋值或 emplace
更改包含的值失败并抛出异常,并且替代方案中存在具有非抛出默认构造函数的类型,则会在 variant
中创建一个该类型的值。 这种决定的好处是,对双重存储的需求减少了。
审查员们几乎一致地讨厌它。 构造一个随机类型被认为太不可预测,并且不符合基本保证的精神。 即使非抛出,所选类型的默认构造函数可能仍然具有不良的副作用。 或者,如果没有这种情况,该类型的值可能对周围的代码具有特殊的意义。 因此,有人认为,variant
应该保持其旧值,或过渡到新值,而不合成其他状态。
另一方面,有些人认为双重存储是不可接受的。 但他们认为原则上不可接受,而不管其使用频率如何。
因此,在赋值和 emplace
上提供强异常安全保证被声明为接受条件。
回顾起来,这是一个正确的决定。 通常不提供强保证的原因是它不具有组合性。 当 X
和 Y
在赋值时提供基本保证时,struct { X x; Y y; };
也是如此。 类似地,当 X
和 Y
具有非抛出赋值时,struct
也是如此。 但这对强保证不成立。
通常的做法是在赋值时提供基本保证,并让用户通过非抛出的 swap
或非抛出的移动赋值来合成一个“强”赋值。 也就是说,给定类型为 X
的 x1
和 x2
,而不是“基本”的 x1 = x2;
,可以使用 X(x2).swap(x1);
或 x1 = X(x2);
。
几乎所有类型都提供非抛出的 swap
或非抛出的移动赋值,因此这效果很好。 几乎所有,除了 variant
,它在一般情况下既没有非抛出的 swap
也没有非抛出的移动赋值。 如果 variant
本身不提供强保证,用户就不可能合成它。
所以它应该提供,而且它也确实提供了。
与 std::variant 的区别
此实现与 std::variant
之间的主要区别是
-
没有 valueless-by-exception 状态:
valueless_by_exception()
始终返回false
。 -
赋值和
emplace
上的强异常安全保证。 -
emplace
首先构造新值,然后销毁旧值; 在单存储的情况下,这转化为构造一个临时对象,然后将其移动到位。 -
提供了一个转换构造函数,例如从
variant<int, float>
到variant<float, double, int>
,作为一个扩展。 -
相反的操作,从
variant<float, double, int>
到variant<int, float>
,作为成员函数subset<U…>
提供。(如果变体的当前状态无法表示,此操作可能会抛出异常。) -
unsafe_get
,一种未经检查的get
和get_if
的替代方案,作为扩展提供。 -
visit_by_index
,一个访问函数,它接受一个变体和多个函数对象,每个替代方案一个,作为扩展提供。 -
C++20 对
std::variant
的添加和更改尚未实现。
与 Boost.Variant 的区别
此库与 std::variant
API 兼容。 因此,它的接口与 Boost.Variant 的不同。 例如,访问是通过 visit
而不是 apply_visitor
执行的。
不支持递归变体。
使用双重存储而不是临时堆备份。 此 variant
始终是“基于堆栈的”,它从不分配,并且从不自行抛出 bad_alloc
。
实现
依赖
此实现仅依赖于 Boost.Config、Boost.Assert 和 Boost.Mp11。
支持的编译器
-
带有
-std=c++11
或更高版本的 GCC 4.8 或更高版本 -
带有
-std=c++11
或更高版本的 Clang 3.9 或更高版本 -
Visual Studio 2015 或更高版本
在 Github Actions 和 Appveyor 上测试。
参考
<boost/variant2/variant.hpp>
概要
namespace boost {
namespace variant2 {
// in_place_type
template<class T> struct in_place_type_t {};
template<class T> constexpr in_place_type_t<T> in_place_type{};
// in_place_index
template<std::size_t I> struct in_place_index_t {};
template<std::size_t I> constexpr in_place_index_t<I> in_place_index{};
// variant
template<class... T> class variant;
// variant_size
template<class T> struct variant_size {};
template<class T> struct variant_size<T const>: variant_size<T> {};
template<class T> struct variant_size<T volatile>: variant_size<T> {};
template<class T> struct variant_size<T const volatile>: variant_size<T> {};
template<class T> struct variant_size<T&>: variant_size<T> {}; // extension
template<class T> struct variant_size<T&&>: variant_size<T> {}; // extension
template<class T>
inline constexpr size_t variant_size_v = variant_size<T>::value;
template<class... T>
struct variant_size<variant<T...>>:
std::integral_constant<std::size_t, sizeof...(T)> {};
// variant_alternative
template<size_t I, class T> struct variant_alternative {};
template<size_t I, class T> struct variant_alternative<I, T const>;
template<size_t I, class T> struct variant_alternative<I, T volatile>;
template<size_t I, class T> struct variant_alternative<I, T const volatile>;
template<size_t I, class T> struct variant_alternative<I, T&>; // extension
template<size_t I, class T> struct variant_alternative<I, T&&>; // extension
template<size_t I, class T>
using variant_alternative_t = typename variant_alternative<I, T>::type;
template<size_t I, class... T>
struct variant_alternative<I, variant<T...>>;
// variant_npos
constexpr std::size_t variant_npos = -1;
// holds_alternative
template<class U, class... T>
constexpr bool holds_alternative(const variant<T...>& v) noexcept;
// get
template<size_t I, class... T>
constexpr variant_alternative_t<I, variant<T...>>&
get(variant<T...>& v);
template<size_t I, class... T>
constexpr variant_alternative_t<I, variant<T...>>&&
get(variant<T...>&& v);
template<size_t I, class... T>
constexpr const variant_alternative_t<I, variant<T...>>&
get(const variant<T...>& v);
template<size_t I, class... T>
constexpr const variant_alternative_t<I, variant<T...>>&&
get(const variant<T...>&& v);
template<class U, class... T>
constexpr U& get(variant<T...>& v);
template<class U, class... T>
constexpr U&& get(variant<T...>&& v);
template<class U, class... T>
constexpr const U& get(const variant<T...>& v);
template<class U, class... T>
constexpr const U&& get(const variant<T...>&& v);
// get_if
template<size_t I, class... T>
constexpr add_pointer_t<variant_alternative_t<I, variant<T...>>>
get_if(variant<T...>* v) noexcept;
template<size_t I, class... T>
constexpr add_pointer_t<const variant_alternative_t<I, variant<T...>>>
get_if(const variant<T...>* v) noexcept;
template<class U, class... T>
constexpr add_pointer_t<U>
get_if(variant<T...>* v) noexcept;
template<class U, class... T>
constexpr add_pointer_t<const U>
get_if(const variant<T...>* v) noexcept;
// unsafe_get (extension)
template<size_t I, class... T>
constexpr variant_alternative_t<I, variant<T...>>&
unsafe_get(variant<T...>& v);
template<size_t I, class... T>
constexpr variant_alternative_t<I, variant<T...>>&&
unsafe_get(variant<T...>&& v);
template<size_t I, class... T>
constexpr const variant_alternative_t<I, variant<T...>>&
unsafe_get(const variant<T...>& v);
template<size_t I, class... T>
constexpr const variant_alternative_t<I, variant<T...>>&&
unsafe_get(const variant<T...>&& v);
// relational operators
template<class... T>
constexpr bool operator==(const variant<T...>& v, const variant<T...>& w);
template<class... T>
constexpr bool operator!=(const variant<T...>& v, const variant<T...>& w);
template<class... T>
constexpr bool operator<(const variant<T...>& v, const variant<T...>& w);
template<class... T>
constexpr bool operator>(const variant<T...>& v, const variant<T...>& w);
template<class... T>
constexpr bool operator<=(const variant<T...>& v, const variant<T...>& w);
template<class... T>
constexpr bool operator>=(const variant<T...>& v, const variant<T...>& w);
// swap
template<class... T>
void swap(variant<T...>& v, variant<T...>& w) noexcept( /*see below*/ );
// visit
template<class R = /*unspecified*/, class F, class... V>
constexpr /*see below*/ visit(F&& f, V&&... v);
// visit_by_index (extension)
template<class R = /*unspecified*/, class V, class... F>
constexpr /*see below*/ visit_by_index(V&& v, F&&... f);
// monostate
struct monostate {};
constexpr bool operator==(monostate, monostate) noexcept { return true; }
constexpr bool operator!=(monostate, monostate) noexcept { return false; }
constexpr bool operator<(monostate, monostate) noexcept { return false; }
constexpr bool operator>(monostate, monostate) noexcept { return false; }
constexpr bool operator<=(monostate, monostate) noexcept { return true; }
constexpr bool operator>=(monostate, monostate) noexcept { return true; }
// stream insertion (extension)
template<class Ch, class Tr, class... T>
std::basic_ostream<Ch, Tr>&
operator<<( std::basic_ostream<Ch, Tr>& os, variant<T...> const& v );
template<class Ch, class Tr>
std::basic_ostream<Ch, Tr>&
operator<<( std::basic_ostream<Ch, Tr>& os, monostate const& v );
// bad_variant_access
class bad_variant_access;
} // namespace variant2
} // namespace boost
variant
namespace boost {
namespace variant2 {
template<class... T> class variant
{
public:
// constructors
constexpr variant() noexcept( /*see below*/ );
constexpr variant( variant const & r ) noexcept( /*see below*/ );
constexpr variant( variant&& r ) noexcept( /*see below*/ );
template<class U>
constexpr variant( U&& u ) noexcept( /*see below*/ );
template<class U, class... A>
constexpr explicit variant( in_place_type_t<U>, A&&... a );
template<class U, class V, class... A>
constexpr explicit variant( in_place_type_t<U>,
std::initializer_list<V> il, A&&... a );
template<size_t I, class... A>
constexpr explicit variant( in_place_index_t<I>, A&&... a );
template<size_t I, class V, class... A>
constexpr explicit variant( in_place_index_t<I>,
std::initializer_list<V> il, A&&... a );
// destructor
~variant();
// assignment
constexpr variant& operator=( variant const & r ) noexcept( /*see below*/ );
constexpr variant& operator=( variant&& r ) noexcept( /*see below*/ );
template<class U> constexpr variant& operator=( U&& u ) noexcept( /*see below*/ );
// modifiers
template<class U, class... A>
constexpr U& emplace( A&&... a );
template<class U, class V, class... A>
constexpr U& emplace( std::initializer_list<V> il, A&&... a );
template<size_t I, class... A>
constexpr variant_alternative_t<I, variant<T...>>&
emplace( A&&... a );
template<size_t I, class V, class... A>
constexpr variant_alternative_t<I, variant<T...>>&
emplace( std::initializer_list<V> il, A&&... a );
// value status
constexpr bool valueless_by_exception() const noexcept;
constexpr size_t index() const noexcept;
static constexpr bool uses_double_storage() noexcept;
// swap
void swap( variant& r ) noexcept( /*see below*/ );
// converting constructors (extension)
template<class... U> variant( variant<U...> const& r )
noexcept( /*see below*/ );
template<class... U> variant( variant<U...>&& r )
noexcept( /*see below*/ );
// subset (extension)
template<class... U> constexpr variant<U...> subset() & ;
template<class... U> constexpr variant<U...> subset() && ;
template<class... U> constexpr variant<U...> subset() const& ;
template<class... U> constexpr variant<U...> subset() const&& ;
};
} // namespace variant2
} // namespace boost
在以下描述中,设 i
在 [0, sizeof…(T))
范围内,并且 Ti
是 T…
中的第 i
个类型。
构造函数
constexpr variant() noexcept( std::is_nothrow_default_constructible_v<T0> );
-
- 影响
-
构造一个
variant
,其中包含类型为T0
的值初始化值。 - 确保
-
index() == 0
. - 抛出
-
T0
的值初始化抛出的任何异常。 - 备注
-
除非
std::is_default_constructible_v<T0>
为true
,否则此函数不参与重载解析。
constexpr variant( variant const & w )
noexcept( mp_all<std::is_nothrow_copy_constructible<T>...>::value );
-
- 影响
-
初始化变体以保存与
w
相同的备选项和值。 - 抛出
-
包含的值的初始化抛出的任何异常。
- 备注
-
除非所有
i
的std::is_copy_constructible_v<Ti>
为true
,否则此函数不参与重载解析。
constexpr variant( variant&& w )
noexcept( mp_all<std::is_nothrow_move_constructible<T>...>::value );
-
- 影响
-
初始化变体以保存与
w
相同的备选项和值。 - 抛出
-
包含的值的移动初始化抛出的任何异常。
- 备注
-
除非所有
i
的std::is_move_constructible_v<Ti>
为true
,否则此函数不参与重载解析。
template<class U> constexpr variant( U&& u ) noexcept(/*see below*/);
-
设
Tj
是如下确定的类型:为每个备选类型Ti
构建一个虚构函数FUN(Ti)
。 通过重载解析为表达式FUN(std::forward<U>(u))
选择的重载FUN(Tj)
定义了备选项Tj
,它是构造后包含的值的类型。- 影响
-
初始化
*this
以保存备选类型Tj
,并从std::forward<U>(u)
初始化包含的值。 - 确保
-
holds_alternative<Tj>(*this)
. - 抛出
-
包含的值的初始化抛出的任何异常。
- 备注
-
noexcept
内部的表达式等效于std::is_nothrow_constructible_v<Tj, U>
。 除非以下情况,否则此函数不参与重载解析-
sizeof…(T)
为非零, -
std::is_same_v<std::remove_cvref_t<U>, variant>
为false
, -
std::remove_cvref_t<U>
既不是in_place_type_t
的特化,也不是in_place_index_t
的特化, -
std::is_constructible_v<Tj, U>
为true
,并且 -
表达式
FUN(std::forward<U>(u))
格式良好。
-
template<class U, class... A>
constexpr explicit variant( in_place_type_t<U>, A&&... a );
-
- 影响
-
使用参数
std::forward<A>(a)…
初始化类型为U
的包含的值。 - 确保
-
holds_alternative<U>(*this)
. - 抛出
-
包含的值的初始化抛出的任何异常。
- 备注
-
除非
T…
中恰好出现一次U
且std::is_constructible_v<U, A…>
为 true,否则此函数不参与重载解析。
template<class U, class V, class... A>
constexpr explicit variant( in_place_type_t<U>, std::initializer_list<V> il,
A&&... a );
-
- 影响
-
使用参数
il
、std::forward<A>(a)…
初始化类型为U
的包含的值。 - 确保
-
holds_alternative<U>(*this)
. - 抛出
-
包含的值的初始化抛出的任何异常。
- 备注
-
除非
T…
中恰好出现一次U
且std::is_constructible_v<U, initializer_list<V>&, A…>
为true
,否则此函数不参与重载解析。
template<size_t I, class... A>
constexpr explicit variant( in_place_index_t<I>, A&&... a );
-
- 影响
-
使用参数
std::forward<A>(a)…
初始化类型为TI
的包含的值。 - 确保
-
index() == I
. - 抛出
-
包含的值的初始化抛出的任何异常。
- 备注
-
除非
I < sizeof…(T)
且std::is_constructible_v<TI, A…>
为true
,否则此函数不参与重载解析。
template<size_t I, class V, class... A>
constexpr explicit variant( in_place_index_t<I>, std::initializer_list<V> il,
A&&... a );
-
- 影响
-
使用参数
il
、std::forward<A>(a)…
初始化类型为TI
的包含的值。 - 确保
-
index() == I
. - 抛出
-
包含的值的初始化抛出的任何异常。
- 备注
-
除非
I < sizeof…(T)
且std::is_constructible_v<TI, initializer_list<V>&, A…>
为true
,否则此函数不参与重载解析。
析构函数
~variant();
-
- 影响
-
销毁当前包含的值。
赋值
constexpr variant& operator=( const variant& r )
noexcept( mp_all<std::is_nothrow_copy_constructible<T>...>::value );
-
设
j
为r.index()
。- 影响
-
emplace<j>(get<j>(r))
. - 返回
-
*this
. - 确保
-
index() == r.index()
. - 备注
-
除非所有
i
的std::is_copy_constructible_v<Ti> && std::is_copy_assignable_v<Ti>
为true
,否则此运算符不参与重载解析。
constexpr variant& operator=( variant&& r )
noexcept( mp_all<std::is_nothrow_move_constructible<T>...>::value );
-
设
j
为r.index()
。- 影响
-
emplace<j>(get<j>(std::move(r)))
. - 返回
-
*this
. - 确保
-
index() == r.index()
. - 备注
-
除非所有
i
的std::is_move_constructible_v<Ti> && std::is_move_assignable_v<Ti>
为true
,否则此运算符不参与重载解析。
template<class U> constexpr variant& operator=( U&& u )
noexcept( /*see below*/ );
-
设
Tj
是如下确定的类型:为每个备选类型Ti
构建一个虚构函数FUN(Ti)
。 通过重载解析为表达式FUN(std::forward<U>(u))
选择的重载FUN(Tj)
定义了备选项Tj
,它是构造后包含的值的类型。- 影响
-
emplace<j>(std::forward<U>(u))
. - 返回
-
*this
. - 确保
-
index() == j
. - 备注
-
noexcept
内部的表达式是std::is_nothrow_constructible_v<Tj, U&&>
。 除非以下情况,否则此运算符不参与重载解析-
std::is_same_v<std::remove_cvref_t<T>, variant>
为false
, -
std::is_constructible_v<Tj, U&&> && std::is_assignable_v<Tj&, U&&>
为true
,并且 -
表达式
FUN(std::forward<U>(u))
(其中FUN
是上述虚构函数集)格式良好。
-
修饰符
template<class U, class... A>
constexpr U& emplace( A&&... a );
-
设
I
为U
在T…
中的从零开始的索引。- 影响
-
等效于:
return emplace<I>(std::forward<A>(a)…);
- 备注
-
除非
std::is_constructible_v<U, A&&…>
为true
且U
在T…
中恰好出现一次,否则此函数不应参与重载解析。
template<class U, class V, class... A>
constexpr U& emplace( std::initializer_list<V> il, A&&... a );
-
设
I
为U
在T…
中的从零开始的索引。- 影响
-
等效于:
return emplace<I>(il, std::forward<A>(a)…);
- 备注
-
除非
std::is_constructible_v<U, std::initializer_list<V>&, A&&…>
为true
且U
在T…
中恰好出现一次,否则此函数不应参与重载解析。
template<size_t I, class... A>
constexpr variant_alternative_t<I, variant<T...>>&
emplace( A&&... a );
-
- 要求
-
I < sizeof…(T)
. - 影响
-
初始化一个新的包含的值,就像使用表达式
Ti(std::forward<A>(a)…)
一样,然后销毁当前包含的值。 - 确保
-
index() == I
. - 返回
-
对新的包含的值的引用。
- 抛出
-
除非新包含的值的初始化抛出异常,否则什么也不做。
- 异常安全
-
强。 发生异常时,包含的值不会更改。
- 备注
-
除非
std::is_constructible_v<Ti, A&&…>
为true
,否则此函数不应参与重载解析。
template<size_t I, class V, class... A>
constexpr variant_alternative_t<I, variant<T...>>&
emplace( std::initializer_list<V> il, A&&... a );
-
- 要求
-
I < sizeof…(T)
. - 影响
-
初始化一个新的包含的值,就像使用表达式
Ti(il, std::forward<A>(a)…)
一样,然后销毁当前包含的值。 - 确保
-
index() == I
. - 返回
-
对新的包含的值的引用。
- 抛出
-
除非新包含的值的初始化抛出异常,否则什么也不做。
- 异常安全
-
强。 发生异常时,包含的值不会更改。
- 备注
-
除非
std::is_constructible_v<Ti, std::initializer_list<V>&, A&&…>
为true
,否则此函数不应参与重载解析。
值状态
constexpr bool valueless_by_exception() const noexcept;
-
- 返回
-
false
.
注意
|
提供此函数纯粹是为了与 std::variant 兼容。 |
constexpr size_t index() const noexcept;
-
- 返回
-
活动备选项的从零开始的索引。
static constexpr bool uses_double_storage() noexcept;
-
- 返回
-
如果变体使用双重存储来满足永不无值的保证,因为其中一个备选项不是非抛出移动构造的,则为
true
,否则为false
。
交换
void swap( variant& r ) noexcept( mp_all<std::is_nothrow_move_constructible<T>...,
is_nothrow_swappable<T>...>::value );
-
- 影响
-
-
如果
index() == r.index()
,则调用swap(get<I>(*this), get<I>(r))
,其中I
是index()
。 -
否则,就像
variant tmp(std::move(*this)); *this = std::move(r); r = std::move(tmp);
一样
-
转换构造函数(扩展)
template<class... U> variant( variant<U...> const& r )
noexcept( mp_all<std::is_nothrow_copy_constructible<U>...>::value );
-
- 影响
-
从
r
的包含的值初始化包含的值。 - 抛出
-
包含的值的初始化抛出的任何异常。
- 备注
-
除非
U…
中的所有类型都在T…
中,并且所有Ui
的std::is_copy_constructible_v<Ui>::value
为true
,否则此函数不参与重载解析。
template<class... U> variant( variant<U...>&& r )
noexcept( mp_all<std::is_nothrow_move_constructible<U>...>::value );
-
- 影响
-
从
std::move(r)
的包含的值初始化包含的值。 - 抛出
-
包含的值的初始化抛出的任何异常。
- 备注
-
除非
U…
中的所有类型都在T…
中,并且所有Ui
的std::is_move_constructible_v<Ui>::value
为true
,否则此函数不参与重载解析。
子集(扩展)
template<class... U> constexpr variant<U...> subset() & ;
template<class... U> constexpr variant<U...> subset() const& ;
-
- 返回
-
一个
variant<U…>
,其包含的值从*this
的包含的值进行复制初始化,并且具有相同的类型。 - 抛出
-
-
如果
*this
的活动备选项不在U…
中的类型中,则抛出bad_variant_access
。 -
否则,包含的值的初始化抛出的任何异常。
-
- 备注
-
除非
U…
中的所有类型都在T…
中,并且所有Ui
的std::is_copy_constructible_v<Ui>::value
为true
,否则此函数不参与重载解析。
template<class... U> constexpr variant<U...> subset() && ;
template<class... U> constexpr variant<U...> subset() const&& ;
-
- 返回
-
一个
variant<U…>
,其包含的值从*this
的包含的值进行移动初始化,并且具有相同的类型。 - 抛出
-
-
如果
*this
的活动备选项不在U…
中的类型中,则抛出bad_variant_access
。 -
否则,包含的值的初始化抛出的任何异常。
-
- 备注
-
除非
U…
中的所有类型都在T…
中,并且所有Ui
的std::is_move_constructible_v<Ui>::value
为true
,否则此函数不参与重载解析。
variant_alternative
template<size_t I, class T> struct variant_alternative<I, T const>;
template<size_t I, class T> struct variant_alternative<I, T volatile>;
template<size_t I, class T> struct variant_alternative<I, T const volatile>;
template<size_t I, class T> struct variant_alternative<I, T&>; // extension
template<size_t I, class T> struct variant_alternative<I, T&&>; // extension
-
如果
typename variant_alternative<I, T>::type
存在且为U
,-
variant_alternative<I, T const>::type
为U const
; -
variant_alternative<I, T volatile>::type
为U volatile
; -
variant_alternative<I, T const volatile>::type
为U const volatile
。 -
variant_alternative<I, T&>::type
为U&
。 -
variant_alternative<I, T&&>::type
为U&&
。
否则,这些结构体没有成员
type
。 -
template<size_t I, class... T>
struct variant_alternative<I, variant<T...>>;
-
当
I < sizeof…(T)
时,嵌套类型type
是T…
中第I
个(从零开始)类型的别名。否则,没有成员type
。
holds_alternative
template<class U, class... T>
constexpr bool holds_alternative(const variant<T...>& v) noexcept;
-
- 要求
-
类型
U
在T…
中恰好出现一次。否则,程序格式错误。 - 返回
-
如果
index()
等于T…
中U
的从零开始的索引,则为true
。
get
template<size_t I, class... T>
constexpr variant_alternative_t<I, variant<T...>>&
get(variant<T...>& v);
template<size_t I, class... T>
constexpr variant_alternative_t<I, variant<T...>>&&
get(variant<T...>&& v);
template<size_t I, class... T>
constexpr const variant_alternative_t<I, variant<T...>>&
get(const variant<T...>& v);
template<size_t I, class... T>
constexpr const variant_alternative_t<I, variant<T...>>&&
get(const variant<T...>&& v);
-
- 影响
-
如果
v.index()
为I
,则返回对变体中存储的对象的引用。否则,抛出bad_variant_access
异常。 - 备注
-
除非
I
<sizeof…(T)
,否则这些函数不参与重载解析。
template<class U, class... T>
constexpr U& get(variant<T...>& v);
template<class U, class... T>
constexpr U&& get(variant<T...>&& v);
template<class U, class... T>
constexpr const U& get(const variant<T...>& v);
template<class U, class... T>
constexpr const U&& get(const variant<T...>&& v);
-
- 要求
-
类型
U
在T…
中恰好出现一次。否则,程序格式错误。 - 影响
-
如果
v
保存了类型为U
的值,则返回对该值的引用。否则,抛出bad_variant_access
异常。
get_if
template<size_t I, class... T>
constexpr add_pointer_t<variant_alternative_t<I, variant<T...>>>
get_if(variant<T...>* v) noexcept;
template<size_t I, class... T>
constexpr add_pointer_t<const variant_alternative_t<I, variant<T...>>>
get_if(const variant<T...>* v) noexcept;
-
- 影响
-
指向变体中存储的值的指针,如果
v != nullptr && v->index() == I
。否则,为nullptr
。 - 备注
-
除非
I
<sizeof…(T)
,否则这些函数不参与重载解析。
template<class U, class... T>
constexpr add_pointer_t<U>
get_if(variant<T...>* v) noexcept;
template<class U, class... T>
constexpr add_pointer_t<const U>
get_if(const variant<T...>* v) noexcept;
-
- 要求
-
类型
U
在T…
中恰好出现一次。否则,程序格式错误。 - 影响
-
等效于:
return get_if<I>(v);
,其中I
是T…
中U
的从零开始的索引。
unsafe_get(扩展)
template<size_t I, class... T>
constexpr variant_alternative_t<I, variant<T...>>&
unsafe_get(variant<T...>& v);
template<size_t I, class... T>
constexpr variant_alternative_t<I, variant<T...>>&&
unsafe_get(variant<T...>&& v);
template<size_t I, class... T>
constexpr const variant_alternative_t<I, variant<T...>>&
unsafe_get(const variant<T...>& v);
template<size_t I, class... T>
constexpr const variant_alternative_t<I, variant<T...>>&&
unsafe_get(const variant<T...>&& v);
-
- 要求
-
v.index() == I
. - 返回
-
对变体中存储的对象的引用。
关系运算符
template<class... T>
constexpr bool operator==(const variant<T...>& v, const variant<T...>& w);
-
- 返回
-
v.index() == w.index() && get<I>(v) == get<I>(w)
,其中I
是v.index()
。
template<class... T>
constexpr bool operator!=(const variant<T...>& v, const variant<T...>& w);
-
- 返回
-
!(v == w)
.
template<class... T>
constexpr bool operator<(const variant<T...>& v, const variant<T...>& w);
-
- 返回
-
v.index() < w.index() || (v.index() == w.index() && get<I>(v) < get<I>(w))
,其中I
是v.index()
。
template<class... T>
constexpr bool operator>(const variant<T...>& v, const variant<T...>& w);
-
- 返回
-
w < v
.
template<class... T>
constexpr bool operator<=(const variant<T...>& v, const variant<T...>& w);
-
- 返回
-
v.index() < w.index() || (v.index() == w.index() && get<I>(v) <= get<I>(w))
,其中I
是v.index()
。
template<class... T>
constexpr bool operator>=(const variant<T...>& v, const variant<T...>& w);
-
- 返回
-
w <= v
.
swap
template<class... T>
void swap(variant<T...>& v, variant<T...>& w) noexcept( /*see below*/ );
-
- 影响
-
等效于
v.swap(w)
。
visit
template<class R = /*unspecified*/, class F, class... V>
constexpr /*see below*/ visit(F&& f, V&&... v);
-
- 返回
-
std::forward<F>(f)(get<I>(std::forward<V>(v))…)
,其中I…
是v.index()…
。 - 备注
-
如果显式给出了
R
,例如visit<int>
,则返回类型为R
。 否则,它从F
推导。 要使此推导成功,将F
应用于变体替代项的所有可能方式都必须具有相同的返回类型。
visit_by_index(扩展)
template<class R = /*unspecified*/, class V, class... F>
constexpr /*see below*/ visit_by_index(V&& v, F&&... f);
-
- 要求
-
variant_size<V>::value == sizeof…(F)
,否则程序格式错误。 - 返回
-
std::forward<Fi>(fi)(get<i>(std::forward<V>(v)))
,其中i
是v.index()
并且Fi
和fi
分别是F…
和f…
的第i
个元素。 - 备注
-
如果显式给出了
R
,例如visit_by_index<int>
,则返回类型为R
。否则,它从F…
和V
推导。 将Fi
应用于相应变体替代项的所有方式都必须具有相同的返回类型,才能使此推导成功。
流插入(扩展)
template<class Ch, class Tr, class... T>
std::basic_ostream<Ch, Tr>&
operator<<( std::basic_ostream<Ch, Tr>& os, variant<T...> const& v );
-
- 要求
-
sizeof…(T) != 0
. - 返回
-
os << get<I>(v)
,其中I
是v.index()
。
template<class Ch, class Tr>
std::basic_ostream<Ch, Tr>&
operator<<( std::basic_ostream<Ch, Tr>& os, monostate const& v );
-
- 影响
-
os << "monostate"
. - 返回
-
os
.
bad_variant_access
class bad_variant_access: public std::exception
{
public:
bad_variant_access() noexcept = default;
char const * what() const noexcept
{
return "bad_variant_access";
}
};
<boost/variant2.hpp>
这个便利的头文件包含了 <boost/variant2/variant.hpp>
。
版权和许可
本文档版权归 Peter Dimov 所有,版权所有 2018, 2019,并根据 Boost Software License, Version 1.0 分发。