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 1addressof
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 2return 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 4void 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 6some_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 8is 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 9template 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 10some_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