1. Trang chủ
  2. » Công Nghệ Thông Tin

HandBooks Professional Java-C-Scrip-SQL part 69 doc

5 55 0

Đang tải... (xem toàn văn)

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 5
Dung lượng 21,13 KB

Nội dung

Usage To start using variants in your programs, include the header "boost/variant.hpp". This header includes the entire library, so you don't need to know which individual features to use; later, you may want to reduce the dependencies by only including the relevant files for the problem at hand. When declaring a variant type, we must define the set of types that it will be capable of storing. The most common way to accomplish this is using template arguments. A variant that is capable of holding a value of type int, std::string, or double is declared like this. boost::variant<int,std::string,double> my_first_variant; When the variable my_first_variant is created, it ends up containing a default- constructed int, because int is first among the types that the variant can contain. We can also pass a value that is convertible to one of those types to initialize the variant. boost::variant<int,std::string,double> my_first_variant("Hello world"); At any give time, we can assign a new value, and as long as the new value is unambiguously and implicitly convertible to one of the types that the variant can contain, it works perfectly. my_first_variant=24; my_first_variant=2.52; my_first_variant="Fabulous!"; my_first_variant=0; After the first assignment, the contained value is of type int; after the second, it's a double; after the third, it's a std::string; and then finally, it's back to an int. If we want to see that this is the case, we can retrieve the value using the function boost::get, like so: assert(boost::get<int>(my_first_variant)==0); Note that if the call to get fails (which would happen if my_first_variant didn't contain a value of type int), an exception of type boost::bad_get is thrown. To avoid getting an exception upon failure, we can pass a pointer to a variant to get, in which case get returns a pointer to the value or, if the requested type doesn't match the type of the value in the variant, it returns the null pointer. Here's how it is used: int* val=boost::get<int>(&my_first_variant); assert(val && (*val)==0); The function get is a very direct way of accessing the contained valuein fact, it works just like any_cast does for boost::any. Note that the type must match exactly, including at least the same cv-qualification (const and volatile). However, a more restrictive cv-qualification will succeed. If the type doesn't match and a variant pointer is passed to get, the null pointer is returned. Otherwise, an exception of type bad_get is thrown. const int& i=boost::get<const int>(my_first_variant); Code that relies too heavily on get can quickly become fragile; if we don't know the type of the contained value, we might be tempted to test for all possible combinations, like the following example does. #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); } The function print does its job correctly now, but what if we decide to change the set of types for the variant? Then we will have introduced a subtle bug that won't be caught at compile time; the function print will not print the value of any other types than the ones we've originally anticipated. If we hadn't used a template function, but required an exact signature of a variant, we would risk proliferation of overloads to accommodate the same functionality for different types of variants. The next section discusses the concept of visiting variants, and the problem that (typesafe) visitation solves. Visiting Variants Let's start with an example that explains why using get isn't as robust as one would like. Starting with the code from the previous example, let's alter the types that the variant can contain, and call print with the char value for the variant, too. 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); } This compiles cleanly even though we have added char to the set of types that the variant can contain and the last two lines of the program set a char value and call print. (Note that print is parameterized on the variant type, so it adapts to the new variant definition easily.) Here's the output of running the program: 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! There's a problem showing in that output. Notice that there is no value reported before the final, "My work here is done!" The reason is that as it stands, print doesn't output the value for any types other than those it was originally designed for (std::string, int, and double), yet it compiles and runs cleanly. The value of the variant is simply ignored if its current type isn't among those supported by print. There are more potential problems with using get, such as getting the order of the if-statements right for class hierarchies. Note that this doesn't mean you should avoid using get altogether; it just emphasizes that it's sometimes not the best solution. What would be better here is a mechanism that somehow allows us to state which types of values are acceptable, and have that statement be validated at compile time. This is exactly what the variant visitation mechanism does. By applying a visitor to a variant the compiler ensures that they are fully compatible. Such visitors in Boost.Variant are function objects with function call operators that accept arguments corresponding to the set of types that the variants they visit can contain. Rewriting the now infamous function print as a visitor looks like this: 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'; } }; To make print_visitor a visitor for variants, we have it inherit publicly from boost::static_visitor to get the correct typedef (result_type), and to explicitly state that this class is a visitor type. The class implements three overloaded versions of the function call operator, which accept an int, a std::string, and a double, respectively. To visit a variant, one uses the function boost::apply_visitor(visitor, variant). If we replace the existing calls to print with calls to apply_visitor, we end up with something like this: 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); } Here, we create a print_visitor, named v, and apply it to my_first_ variant after putting each value in it. Because we don't have a function call operator accepting char, this code fails to compile, right? Wrong! A char can be unambiguously converted to an int, so the visitor is compatible with our variant type. This is what we get when running the program. It's a std::string: Hello there! It's an int: 12 It's a double: 1.1 It's an int: 97 We learn two things from thisthe first is that the character a has the ASCII value 97, and the second, more important, lesson is that if a visitor accepts its arguments by value, any implicit conversions will be applied to the values being passed. If we want only the exact types to be compatible with the visitor (and also avoid copying the value from the variant), we must change how the visitor function call operators accept their arguments. The following version of print_visitor only works for the types int, std::string, and double; and any other types that provide an implicit conversion to a reference of one of those types. class print_visitor : public boost::static_visitor<void> {

Ngày đăng: 06/07/2014, 04:20