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

HandBooks Professional Java-C-Scrip-SQL part 52 pdf

12 114 0
Tài liệu đã được kiểm tra trùng lặp

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

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 12
Dung lượng 48,42 KB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

By using some clever internal machinery, the template function addressof ensures that it gets to the actual object and its address.. #include "boost/utility.hpp" class some_class {}; int

Trang 1

addressof

Header: "boost/utility.hpp"

When taking the address of a variable, we typically depend on the returned value to

be, well, the address of the variable However, it's technically possible to overload operator&, which means that evildoers may be on a mission to wreak havoc on your address-dependent code boost::addressof is provided to get the

address anyway, regardless of potential uses and misuses of operator overloading

By using some clever internal machinery, the template function addressof ensures that it gets to the actual object and its address

Usage

To always be sure to get the real address of an object, use boost::addressof

It is defined in "boost/utility.hpp" It is used where operator& would otherwise be used, and it accepts an argument that is a reference to the type whose address should be taken

#include "boost/utility.hpp"

class some_class {};

int main() {

some_class s;

some_class* p=boost::addressof(s);

}

Before seeing more details on how to use addressof, it is helpful to understand why and how operator& may not actually return the address of an object

Quick Lesson for Evildoers

If you really, really, really need to overload operator&, or just want to

experiment with the potential uses of operator overloading, it's actually quite easy When overloading operator&, the semantics are always different from what most users (and functions!) expect, so don't do it just to be cute; do it for a very good reason or not at all That said, here's a code-breaker for you:

class codebreaker {

public:

int operator&() const {

Trang 2

return 13;

}

};

With this class, anyone who tries to take the address of an instance of

codebreaker is handed the magical number 13

template <typename T> void print_address(const T& t) {

std::cout << "Address: " << (&t) << '\n';

}

int main() {

codebreaker c;

print_address(c);

}

It's not hard to do this, but are there good arguments for ever doing it in real code? Probably not, because it cannot be made safe except when using local classes The reason for this is that while it is legal to take the address of an incomplete type, it is undefined behavior to do so on an incomplete class with a user-defined

operator& Because we cannot guarantee that this won't happen, we're better off not overloading operator&

Quick Remedy for Others

Even when operator& is supplied by the class, it is possible to get to the real address of instances of the class addressof performs some clever work[6]

behind the scenes to get to the bottom of the address issue, regardless of any

operator& chicanery If you adjust the function (print_address) to make use of addressof, you'll get what we came here for:

[6]

Also known as an ingenious hack

template <typename T> void print_address(const T& t) {

std::cout << "&t: " << (&t) << '\n';

std::cout << "addressof(t): " << boost::addressof(t) << '\n';

}

When invoked, the function gives this output (or similar, because the exact address differs depending upon your system)

Trang 3

&t: 13

addressof(t): 0012FECB13

That's more like it! If there are scenarios where you know, or suspect, that

operator& is provided by a class but you need to be really sure that you get the actual address (which is unlikely for an overloaded operator& or why else would it be overloaded in the first place?), use addressof

Summary

There are not many potent arguments for overloading operator&,[7] but because

it is possible, some people do it anyway When writing code that relies on

retrieving the actual address of objects, addressof can help by ensuring that the real address is returned When writing generic code, there is no way of telling which types will be operated upon, so if the address of parameterized types needs

to be taken, use addressof

[7]

Custom hardware device drivers notwithstanding

Use addressof when you must retrieve the actual address of an object,

regardless of the semantics for operator&

enable_if

Header: "boost/utility/enable_if.hpp"

Sometimes, we wish to control whether a certain function, or class template

specialization, can take part in the set of available overloads/specializations for overload resolution For example, consider an overloaded function where one version is an ordinary function taking an int argument, and the other is a

templated version that requires that the argument of type T has a nested type called type They might look like this:

Trang 4

void some_func(int i) {

std::cout << "void some_func(" << i << ")\n";

}

template <typename T> void some_func(T t) {

typename T::type variable_of_nested_type;

std::cout <<

"template <typename T> void some_func(" << t << ")\n";

}

Now, imagine what happens when you call some_func somewhere in your code

If the type of the argument is int, the first version is called Assuming that the type is something other than int, the second (templated) version is called

This is fine, as long as that type has a nested type named type, but if it doesn't, this code does not compile Is this really a problem? Well, consider what happens when another integral type is used, like short, or char, or unsigned long

#include <iostream>

void some_func(int i) {

std::cout << "void some_func(" << i << ")\n";

}

template <typename T> void some_func(T t) {

typename T::type variable_of_nested_type;

std::cout <<

"template <typename T> void some_func(" << t << ")\n";

}

int main() {

int i=12;

short s=12;

some_func(i);

some_func(s);

}

When compiling this program, you will get something like the following output from the frustrated compiler:

enable_if_sample1.cpp: In function 'void some_func(T)

[with T = short int]':

enable_if_sample1.cpp:17: instantiated from here

enable_if_sample1.cpp:8: error:

Trang 5

'short int' is not a class, struct, or union type

Compilation exited abnormally with code 1 at Sat Mar 06 14:30:08

There it is The template version of some_func has been chosen as the best overload, but the code in that version is not valid for the type short How could

we have avoided this? Well, we would have liked to only enable the template version of some_func for types with a nested type named type, and to ignore it for those without it We can do that The easiest way, which is not always an option in real life, is to change the return type of the template version like so:

template <typename T> typename T::type* some_func(T t) {

typename T::type variable_of_nested_type;

std::cout <<

"template <typename T> void some_func(" << t << ")\n";

return 0;

}

If you haven't yet studied SFINAE (substitution failure is not an error),[8] chances are that you have a perplexed look on your face right now When compiling with this update, our example compiles cleanly The short is promoted to int, and the first version is called The reason for this surprising behavior is that the

template version of some_func isn't included in the overload resolution set anymore It's excluded because when the compiler sees that the return type of the function requires that type be a nested type of the template type T (lots of types here), it knows that short doesn't fit the bill, so it removes the function template from the overload resolution set This is what Daveed Vandevorde and Nicolai Josuttis have taught us to refer to as SFINAE, and it means that rather than

producing a compiler error, that function simply is not considered as a valid

overload for the type in question If the type has such a nested type, though, it will

be part of the overload set

[8]

See [3] in the Bibliography

class some_class {

public:

typedef int type;

};

int main() {

int i=12;

short s=12;

Trang 6

some_func(i);

some_func(s);

some_func(some_class());

}

The output when running this program is as follows:

void some_func(12)

void some_func(12)

template <typename T> void some_func(T t)

This works, but it's not a pretty sight In this scenario, we had the luxury of playing with a void return type, which we could use for other purposes If that hadn't been the case, we could have added another argument to the function and given it a default value

template <typename T>

void some_func(T t,typename T::type* p=0) {

typename T::type variable_of_nested_type;

std::cout << "template <typename T> void some_func(T t)\n";

}

This version also uses SFINAE to disqualify itself from use with invalid types The problem with both of these solutions is that they're really ugly, we have made them

a part of the public interface, and they only work in some scenarios Boost offers a much cleaner solution, which is both syntactically nicer and offers a much wider range of functionality than our earlier ad hoc solutions

Usage

To use enable_if and disable_if, include

"boost/utility/enable_if.hpp" For the first example, we'll disable the second version of some_func if the type of the argument is integral Type

information such as whether a type is integral is available in another Boost library, Boost.Type_traits The enable_if and disable_if templates both accept a predicate controlling whether to enable or disable the function,

respectively

#include <iostream>

#include "boost/utility/enable_if.hpp"

Trang 7

#include "boost/type_traits.hpp"

void some_func(int i) {

std::cout << "void some_func(" << i << ")\n";

}

template <typename T> void some_func(

T t,typename boost::disable_if<

boost::is_integral<T> >::type* p=0) {

typename T::type variable_of_nested_type;

std::cout << "template <typename T> void some_func(T t)\n";

}

Although this is similar to what we did before, it's expressing something that we couldn't have done as easily using our direct approach, and this also has the

advantage of documenting important information about the function in its

signature When reading this, it is clear that the function requires that the type T is not an integral type It would be even better if we could enable it (and document it accordingly) only for types with a nested type, type, and we can do that if we use another library, Boost.Mpl.[9] Check this out:

[9]

Boost.Mpl is beyond the scope of this book Visit http://www.boost.org for more information on Mpl Also, get your hands on David Abrahams's and Aleksey

Gurtovoy's book, C++ Template Metaprogramming!

#include <iostream>

#include "boost/utility/enable_if.hpp"

#include "boost/type_traits.hpp"

#include "boost/mpl/has_xxx.hpp"

BOOST_MPL_HAS_XXX_TRAIT_DEF(type)

void some_func(int i) {

std::cout << "void some_func(" << i << ")\n";

}

template <typename T> void some_func(T t,

typename boost::enable_if<has_type<T> >::type* p=0) {

typename T::type variable_of_nested_type;

std::cout << "template <typename T> void some_func(T t)\n";

}

This is very cool indeed! We are now disabling the template version of

some_func when there is no nested type, type, in T, and we explicitly

document that this is a requirement for the function in its signature The trick here

Trang 8

is to use a very nifty feature of Boost.Mpl that can test whether a certain nested type (or typedef) exists in an arbitrary type T Using the macro invocation,

BOOST_MPL_HAS_XXX_TRAIT_DEF(type), we define a new trait called has_type, which we use in the function some_func as the predicate for

enable_if If the predicate yields TRue, the function is part of the overload set;

if it yields false, it is excluded

It's also possible to wrap the return type rather than add an extra (defaulted)

argument The equivalent of our latest and greatest some_func, but using

enable_if in the return type, looks like this

template <typename T> typename

boost::enable_if<has_type<T>,void>::type

some_func(T t) {

typename T::type variable_of_nested_type;

std::cout << "template <typename T> void some_func(T t)\n";

}

If you need to return the type that you need to enable or disable on, using

enable_if and disable_if on the return type makes more sense than adding

a defaulted argument Also, there is a chance that someone actually provides a value instead of that default argument, which breaks the code Sometimes, class template specializations need to be enabled or disabled, and

enable_if/disable_if work for those cases too The difference is that for class templates, we must give the primary template some special treatmentan

additional template parameter Consider a class template with a member function max that returns an int:

template <typename T> class some_class {

public:

int max() const {

std::cout << "some_class::max() for the primary template\n";

return std::numeric_limits<int>::max();

}

};

Suppose we decide that for all arithmetic types (integral types and floating point types), there should be a specialization available that returns max as the maximum value that the type can hold We'll thus need std::numeric_limits for the

Trang 9

template type T, and we want all other types to use the primary template To make this work, we must add a template parameter to the primary template, which has a default type of void (this means that users don't have to deal explicitly with this type) This results in the following primary template:

template <typename T,typename Enable=void> class some_class {

public:

int max() const {

std::cout << "some_class::max() for the primary template\n";

return std::numeric_limits<int>::max();

}

};

We've now paved the way for providing a more specialized version, to be enabled

if the type is arithmetic That trait is available via the Boost.Type_traits library Here's the specialization:

template <typename T> class some_class<T,

typename boost::enable_if<boost::is_arithmetic<T> >::type> {

public:

T max() const {

std::cout << "some_class::max() with an arithmetic type\n";

return std::numeric_limits<T>::max();

}

};

This version is only enabled when instantiated with a type that is arithmeticthat is, when the trait is_arithmetic yields true This works because

boost::enable_if<false>::type is void, matching the primary

template specialization The following program tests these templates with various types:

#include <iostream>

#include <string>

#include <limits>

#include "boost/utility/enable_if.hpp"

#include "boost/type_traits.hpp"

// Definition of the template some_class omitted

int main() {

std::cout << "Max for std::string: " <<

Trang 10

some_class<std::string>().max() << '\n';

std::cout << "Max for void: " <<

some_class<void>().max() << '\n';

std::cout << "Max for short: " <<

some_class<short>().max() << '\n';

std::cout << "Max for int: " <<

some_class<int>().max() << '\n';

std::cout << "Max for long: " <<

some_class<long>().max() << '\n';

std::cout << "Max for double: " <<

some_class<double>().max() << '\n';

}

We'd expect the first two uses of some_class to instantiate the primary

template, and the rest to instantiate the specialization for arithmetic types Running the program shows that this is indeed the case

some_class::max() for the primary template

Max for std::string: 2147483647

some_class::max() for the primary template

Max for void: 2147483647

some_class::max() with an arithmetic type

Max for short: 32767

some_class::max() with an arithmetic type

Max for int: 2147483647

some_class::max() with an arithmetic type

Max for long: 2147483647

some_class::max() with an arithmetic type

Max for double: 1.79769e+308

That's all there is to it! Enabling and disabling overloaded functions and template specializations has heretofore required some tricky programming, which most who read the code would not fully understand By using enable_if and

disable_if, the solution becomes easier to code and read, and automatically captures the type requirements right in the declaration In the preceding example,

we use the template enable_if, which expects that the condition has a nested definition called value This is true for most metaprogramming-enabled types, but it certainly isn't for integral constant expressions, for example When there is

no nested type called value, use enable_if_c instead, which expects an integral constant expression Naively using the trait is_arithmetic and

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

TỪ KHÓA LIÊN QUAN