Universal Less<> for Pointers in C++ Standard

Universal less for pointers in C++ standard

Two pointers can be compared with using the comparison function objects less, greater etc. Otherwise, using blanket operator< etc, this is only possible if the pointers point to elements of the same array object or one past the end. Otherwise, results are unspecified.

20.3.3/8 in C++03

For templates greater, less, greater_equal, and less_equal, the specializations for any
pointer type yield a total order, even if the built-in operators <, >, <=, >= do not.

No need to explicitly specialize and manually casting to size_t: That would lower the portability even, since the mapping of reinterpret_cast from pointers to integers is implementation defined and is not required to yield any order.


Edit: For a more detailed answer, see this one.

Pointers as keys in map C++ STL

The default implementation will compare the addresses stored by the pointers, so different objects will be considered as different keys. However, the logical state of the object will not be considered. For example, if you use std::string * as the key, two different std::string objects with the same text of "Hello" would be considered a different key! (When stored in the map by their addresses)

It's ok to use pointers as keys so long as you understand the important difference above.

Using std::less with nullptr

Introduction

This question really boils down to whether the use of the less-than relational operator on pointer types where one operand is a nullptr will yield the "expected" result; which sadly isn't the case.

The result is unspecified.

Note: Do mind that std::less guarantees a total order; meaning that even if the result, when using the function object, is unspecified, it must yield the same unspecified value on each invocation.


What does the International Standard (N3337) say?

5.9p2 Relational operators [expr.rel]

Pointers to objects or functions of the same type (after pointer conversions) can be compared, with a result defined as follows:


  • If two pointers p and q of the same type point to the same object or function, or both point one past the end of the same array, or are both null, then p<=q and p>=q both yield true and p<q and p>q both yield false.

  • If two pointers p and q of the same type point to different objects that are not members of the same object or elements of the same array or to different functions, or if only one of them is null, the results of p<q, p>q, p<=q, and p>=q are unspecified.

  • If two pointers point to non-static data members of the same object, or to subobjects or array elements of such members, recursively, the pointer to the later declared member compares greater provided the two members have the same access control (Clause 11) and provided their class is not a union.

  • If two pointers point to non-static data members of the same object with different access control (Clause 11) the result is unspecified.

  • If two pointers point to non-static data members of the same union object, they compare equal (after conversion to void*, if necessary). If two pointers point to elements of the same array or one beyond the end of the array, the pointer to the object with the higher subscript compares higher.

  • Other pointer comparisons are unspecified.

20.8.5p8 Comparison [comparision]

For templates greater, less, greater_equal, and less_equal, the specializations for any pointer type yield a total order, even if the built-in operators <, >, <=, >= do not.


So, what is the standard really saying?

T * p = new T;
T * q = nullptr;


What is the verdict for p < q?

Since p and q don't point to different elements of the same array (including the element one past the last element of an array), and both don't point to non-static data members of the same object; the result when doing p < q (and p > q) is unspecified.

bool a = p < q;  // unspecified
bool b = p < q; // unspecified

assert (a == b); // can fire


What about std::less?

However, when using std::less we are guaranteed a total order - which effectively means that the below assertion cannot fire (standard-20.8.5p8).

std::less<T*> comp;

bool a = comp (p, q); // unspecified
bool b = comp (p, q); // unspecified

assert (a == b); // can not fire

Should I use std::function or a function pointer in C++?

In short, use std::function unless you have a reason not to.

Function pointers have the disadvantage of not being able to capture some context. You won't be able to for example pass a lambda function as a callback which captures some context variables (but it will work if it doesn't capture any). Calling a member variable of an object (i.e. non-static) is thus also not possible, since the object (this-pointer) needs to be captured.(1)

std::function (since C++11) is primarily to store a function (passing it around doesn't require it to be stored). Hence if you want to store the callback for example in a member variable, it's probably your best choice. But also if you don't store it, it's a good "first choice" although it has the disadvantage of introducing some (very small) overhead when being called (so in a very performance-critical situation it might be a problem but in most it should not). It is very "universal": if you care a lot about consistent and readable code as well as don't want to think about every choice you make (i.e. want to keep it simple), use std::function for every function you pass around.

Think about a third option: If you're about to implement a small function which then reports something via the provided callback function, consider a template parameter, which can then be any callable object, i.e. a function pointer, a functor, a lambda, a std::function, ... Drawback here is that your (outer) function becomes a template and hence needs to be implemented in the header. On the other hand you get the advantage that the call to the callback can be inlined, as the client code of your (outer) function "sees" the call to the callback will the exact type information being available.

Example for the version with the template parameter (write & instead of && for pre-C++11):

template <typename CallbackFunction>
void myFunction(..., CallbackFunction && callback) {
...
callback(...);
...
}

As you can see in the following table, all of them have their advantages and disadvantages:























































function ptrstd::functiontemplate param
can capture context variablesno1yesyes
no call overhead (see comments)yesnoyes
can be inlined (see comments)nonoyes
can be stored in a class memberyesyesno2
can be implemented outside of headeryesyesno
supported without C++11 standardyesno3yes
nicely readable (my opinion)noyes(yes)

Managing objective-C objects with c++ std::unique_ptr or std::shared_ptr

But Objective-C objects still are more or less manually managed, and RAII idiom is entirely absent from the language.

I think this would seem to answer your question anyhow. Because Objective-C objects are reference counted, they already fulfill the purpose smart pointers were created for: to divorce or tie the lifetime of an object from the scope of the method it's contained in. scoped_ptrs can be recreated with autorelease pools, and shared_ptrs with -retain--release or strong references.

But saying no is boring. If you really want to mingle Objective-C and C++ like this, we'll need to first loosen the definition of "Objective-C object". The runtime recognizes anything with an isa sitting as its first member as an object, and we can take advantage of that and write a simple C++ class with a corresponding object interface so it can be messaged:

@interface CFIObject : NSObject
- (void)doSomething;
@end

struct CFIObject_cxx {
Class isa;
public:
CFIObject_cxx() : isa([CFIObject class]) {}
~CFIObject_cxx() { printf("I'm dying!"); }
};

@implementation CFIObject
- (void)doSomething {
NSLog("I did something.");
}
@end

We can now instantiate an instance of our C++ object and wrap it in a smart pointer, which I'm going to purposefully split into two methods to illustrate the lifetime of the object:

void func() {
// Instantiate a smart pointer with our fake object.
std::unique_ptr<CFIObject_cxx> cppObj (new CFIObject_cxx());
id obj = (__bridge id)(cppObj.get());
// The runtime thinks we're an object.
[obj doSomething];
// aaaand, it's out of scope.
}

int main(int argc, const char **argv) {
func();
return 0;
}

As one would expect, this prints:

2013-12-22 17:23:22.681 Test[77528:303] I did something
I'm dying!

to the console.

If need be, the destructor could be outfitted to call through to -dealloc to simulate proper object destruction, but I hope you see that all of this is just wholly unnecessary, especially with ARC getting smarter with each release of CLANG.

What is std::decay and when it should be used?

<joke>It's obviously used to decay radioactive std::atomic types into non-radioactive ones.</joke>

N2609 is the paper that proposed std::decay. The paper explains:

Simply put, decay<T>::type is the identity type-transformation except
if T is an array type or a reference to a function type. In those
cases the decay<T>::type yields a pointer or a pointer to a function,
respectively.

The motivating example is C++03 std::make_pair:

template <class T1, class T2> 
inline pair<T1,T2> make_pair(T1 x, T2 y)
{
return pair<T1,T2>(x, y);
}

which accepted its parameters by value to make string literals work:

std::pair<std::string, int> p = make_pair("foo", 0);

If it accepted its parameters by reference, then T1 will be deduced as an array type, and then constructing a pair<T1, T2> will be ill-formed.

But obviously this leads to significant inefficiencies. Hence the need for decay, to apply the set of transformations that occurs when pass-by-value occurs, allowing you to get the efficiency of taking the parameters by reference, but still get the type transformations needed for your code to work with string literals, array types, function types and the like:

template <class T1, class T2> 
inline pair< typename decay<T1>::type, typename decay<T2>::type >
make_pair(T1&& x, T2&& y)
{
return pair< typename decay<T1>::type,
typename decay<T2>::type >(std::forward<T1>(x),
std::forward<T2>(y));
}

Note: this is not the actual C++11 make_pair implementation - the C++11 make_pair also unwraps std::reference_wrappers.

What is the difference between const int*, const int * const, and int const *?

Read it backwards (as driven by Clockwise/Spiral Rule):

  • int* - pointer to int
  • int const * - pointer to const int
  • int * const - const pointer to int
  • int const * const - const pointer to const int

Now the first const can be on either side of the type so:

  • const int * == int const *
  • const int * const == int const * const

If you want to go really crazy you can do things like this:

  • int ** - pointer to pointer to int
  • int ** const - a const pointer to a pointer to an int
  • int * const * - a pointer to a const pointer to an int
  • int const ** - a pointer to a pointer to a const int
  • int * const * const - a const pointer to a const pointer to an int
  • ...

And to make sure we are clear on the meaning of const:

int a = 5, b = 10, c = 15;

const int* foo; // pointer to constant int.
foo = &a; // assignment to where foo points to.

/* dummy statement*/
*foo = 6; // the value of a can´t get changed through the pointer.

foo = &b; // the pointer foo can be changed.

int *const bar = &c; // constant pointer to int
// note, you actually need to set the pointer
// here because you can't change it later ;)

*bar = 16; // the value of c can be changed through the pointer.

/* dummy statement*/
bar = &a; // not possible because bar is a constant pointer.

foo is a variable pointer to a constant integer. This lets you change what you point to but not the value that you point to. Most often this is seen with C-style strings where you have a pointer to a const char. You may change which string you point to but you can't change the content of these strings. This is important when the string itself is in the data segment of a program and shouldn't be changed.

bar is a constant or fixed pointer to a value that can be changed. This is like a reference without the extra syntactic sugar. Because of this fact, usually you would use a reference where you would use a T* const pointer unless you need to allow NULL pointers.

What are the operations supported by raw pointer and function pointer in C/C++?

For both function and object pointers, they compile but their result is only guaranteed to be consistent for addresses to sub-objects of the same complete object (you may compare the addresses of two members of a class or array) and if you compare a function or object against itself.

Using std::less<>, std::greater<> and so on will work with any pointer type, and will give consistent results, even if the result of the respective built-in operator is unspecified:

void f() { }
void g() { }

int main() {
int a, b;

///// not guaranteed to pass
assert((&a < &b) == (&a < &b));

///// guaranteed to pass
std::less<int*> lss1;
assert(lss1(&a, &b) == lss1(&a, &b));
// note: we don't know whether lss1(&a, &b) is true or false.
// But it's either always true or always false.

////// guaranteed to pass
int c[2];
assert((&c[0] < &c[1]) == (&c[0] < &c[1]));
// in addition, the smaller index compares less:
assert(&c[0] < &c[1]);

///// not guaranteed to pass
assert((&f < &g) == (&f < &g));

///// guaranteed to pass
assert((&g < &g) == (&g < &g));
// in addition, a function compares not less against itself.
assert(!(&g < &g));

///// guaranteed to pass
std::less<void(*)()> lss2;
assert(lss2(&f, &g) == lss2(&f, &g));
// note: same, we don't know whether lss2(&f, &g) is true or false.

///// guaranteed to pass
struct test {
int a;
// no "access:" thing may be between these!
int b;

int c[1];
// likewise here
int d[1];

test() {
assert((&a < &b) == (&a < &b));
assert((&c[0] < &d[0]) == (&c[0] < &d[0]));

// in addition, the previous member compares less:
assert((&a < &b) && (&c[0] < &d[0]));
}
} t;
}

Everything of that should compile though (although the compiler is free to warn about any code snippet it wants).


Since function types have no sizeof value, operations that are defined in terms of sizeof of the pointee type will not work, these include:

void(*p)() = ...;
// all won't work, since `sizeof (void())` won't work.
// GCC has an extension that treats it as 1 byte, though.
p++; p--; p + n; p - n;

The unary + works on any pointer type, and will just return the value of it, there is nothing special about it for function pointers.

+ p; // works. the result is the address stored in p.

Finally note that a pointer to a function pointer is not a function pointer anymore:

void (**pp)() = &p;
// all do work, because `sizeof (void(*)())` is defined.
pp++; pp--; pp + n; pp - n;

What is a void pointer and what is a null pointer?

The two concepts are orthogonal:

  1. A void pointer, (void *) is a raw pointer to some memory location.
  2. A null pointer is a special pointer that doesn't point to anything, by definition. It can be a pointer to any type, void or otherwise.

A void pointer can be null or not:

void *void_ptr1 = nullptr;
void *void_ptr2 = malloc(42);
void *void_ptr3 = new Foo; // void * can point to almost anything
void *void_ptr4 = (char*)void_ptr3 + 1; // even somewhere inside an object

A non-void pointer can also be null or not:

Foo *f = nullptr;
Foo *g = new Foo;

Passing optional parameter by reference in c++

The default argument of a (mutable) reference must be an l-value. The best I can think of, without overloading, is

static double _dummy_foobar;
void foo(double &bar, double &foobar = _dummy_foobar)


Related Topics



Leave a reply



Submit