For a built in type integer array say
int a[10];
int i = 2;
a[i] = 10;
alternatively
i[a] = 10;
because
a[i] is a postfix expression that is *(a+i) or *(i+a) because commutative property of addition.
I want to achieve that for a userdefined type say
class Dummy
{
//
};
Is it possible?
If yes then how?
If no then why?
EDIT :-
I know it is ugly but following code compiles :-
g++ -dumpversion
4.3.3
#include <stdio.h>
#include<iostream>
#include <string.h>
#include <malloc.h>
using namespace std;
int main()
{
string ArrayS[10];
2[ArrayS] = "ADASD" ;
cout << 2[ArrayS] << endl;
return 0;
}
It is impossible because "operator[] shall be a non-static member function with exactly one parameter" (standard §13.5.5/1), so you cannot define it such that the first argument is of native scalar type.
(Furthermore, a nonstatic operator overload call is interpreted as a member call, so the first operand cannot be implicitly converted, unlike a free function overload. This is one reason why free function overloads are preferred when possible.)
For better or worse, index[ object ] is a way to ensure that no operator[] overload gets called.
However.
"The expression E1[E2] is identical (by definition) to *((E1)+(E2))" (§5.2.1) and operator+ can be overloaded so long as one side is not native type. This leaves two options vulnerabilities: the "array" must be a class, or the "index" must be a class or enum.
You would then have to define a proxy type to hold the result of "addition," which defines an operator* overload. GCC does not support this, however. I'll look deeper into other platforms and references.
Edit: Ah, §13.6/13 overrides 5.2.1 and declares that, for the sake of interpreting an expression involving class or enumeration type, there are functions T& operator[](std::ptrdiff_t, T*); and T& operator[](T*, std::ptrdiff_t);. So that's that.
With C++, nothing is impossible. It is however, a terrible terrible idea. Don't do this.
#include <memory>
#include <stdlib.h>
#include <stdio.h>
void *aligned_malloc( size_t bytes, size_t alignment ) {
void *p = malloc( bytes + alignment ), *pa = reinterpret_cast<void*>( reinterpret_cast<size_t>(p) + alignment &- alignment );
reinterpret_cast<void**>(pa)[-1] = p;
return pa;
}
void aligned_free( void *pa ) {
void *p = reinterpret_cast<void**>(pa)[-1];
free( p );
}
struct SupportReverseIndexer
{
class IndexerReversal
{
static const size_t alignment;
friend struct SupportReverseIndexer;
friend class std::auto_ptr<IndexerReversal>;
struct SupportReverseIndexer* const m_parent;
IndexerReversal(struct SupportReverseIndexer* parent) : m_parent(parent) {}
void* operator new(size_t bytes) { return aligned_malloc(bytes, alignment); }
void operator delete(void* p) { aligned_free(p); }
static struct SupportReverseIndexer* getParent(IndexerReversal* pThis)
{
size_t iThis = reinterpret_cast<size_t>(pThis);
iThis += alignment >> 1;
iThis &= ~(alignment - 1);
return reinterpret_cast<IndexerReversal*>(iThis)->m_parent;
}
public:
operator size_t() { struct SupportReverseIndexer* const parent = getParent(this); return parent->indexer(this-parent->ir.get()); }
};
SupportReverseIndexer() : ir(new IndexerReversal(this)) {}
operator IndexerReversal*() { return ir.get(); }
private:
std::auto_ptr<IndexerReversal> ir;
size_t indexer(size_t index) { printf("Custom operator[] called, index = %i\n", index); return index; }
};
const size_t SupportReverseIndexer::IndexerReversal::alignment = 0x10000 * sizeof(SupportReverseIndexer::IndexerReversal);
int main(void)
{
SupportReverseIndexer sri;
int a = sri[2];
a = 3[sri];
a = (-5)[sri];
return 0;
}
No, really, DON'T DO THIS!!!!!
It is completely impossible to do. Potatoswatter correctly points out you couldn't possibly define any operator such as operator[](int, T), so overloading for an integer on the left is impossible.
But consider that this works:
struct foo
{
operator const foo*() const
{
return this;
}
};
int main()
{
foo f;
5[f]; // UB
}
Is there a way to utilize this? No:
5.2.1 Subscripting
A postfix expression followed by an expression in square brackets is a postfix expression. One of the expressions shall have the type “pointer to T” and the other shall have enumeration or integral type. The result is an lvalue of type “T.” The type “T” shall be a completely-defined object type.56) The expression E1[E2] is identical (by definition) to *((E1)+(E2)).
With E1 being an integral type, E2 must be a pointer type. So we can't inject user-behavior there.
The only thing left is + and *. We can't change operator+ for an integral type and a pointer, because that's defined by the language. The result of E1 + E2 is going to be a pointer, and we can't define operator* for a pointer either.
Therefore, injecting user-defined behavior is impossible.
a[i] = 10;
i[a] = 10;
because a[i] is a postfix expression that is *(a+i) or *(i+a) because commutative property of addition.
Yes. But a[i] == i[a] == *(a+i) == *(i+a) is because the notation a[i] in C is a syntactic sugar for the latter pointer arithmetic. Keyword here "in C". C++ in large tries to part ways from the pointer arithmetic. Thus the a[i] as a syntactic sugar is only supported on the POD for backward compatibility with the C. But that does not work on any C++ objects since the operations [] has clear semantic specified and it doesn't allow the C-like syntax tricks.
In the end, do not do it. The trick is an obscure remnant of the older times and doesn't have a single good reason to be reincarnated.
Any Dummy that was declared and used as an array will work the same way, if Dummy can be implicitly cast to an integer.
You will need to define the Dummy::operator[](const Dummy& ref) to exhibit the desired property. For instance:
#include <iostream>
class dummy
{
public:
int operator[](const dummy& ref) { return 0; }
};
int main(int argc, char *argv[])
{
dummy d1, d2;
std::cout << (d1[d2] == d2[d1] ? "Yes" : "No");
}
Of course this is probably not exactly what you're looking for, but the tricky bit will be to replace the return 0; and possibly the return type with something that you have to define and that satisfies your intent.
Why exactly would you want this? Is it just a mental exercise?
Related
Running the program below on my computer
#include <iostream>
class Int {
public:
Int(int x) : val{x} {}
Int operator++() {
std::cout << "Int::operator++()\n";
return ++val;
}
friend Int operator+(Int a, Int b) {
std::cout << "operator+(Int, Int)\n";
return a.val + b.val;
}
friend Int operator*(Int a, Int b) {
std::cout << "operator*(Int, Int)\n";
return a.val * b.val;
}
private:
int val;
};
int main()
{
Int a = 1, b = 2;
b = ++a + b * b;
return 0;
}
I got this output:
operator*(Int, Int)
Int::operator++()
operator+(Int, Int)
As far as I known, the prefix ++ has higher precedence than binary *. But in the output shown above, the prefix ++ is called after the binary *! Is it because the compiler treats the operator+ as a function call (which leads to Unspecified Behavior)? Can I always consider an overloaded operator as a function (which makes the behavior of x = x++ well-defined when x is an Int)?
Thanks!
Is it because the compiler treats the operator+ as a function call (which leads to Unspecified Behavior)?
Yes. But note that it doesn't matter if ++a is evaluated after b * b, because in the end, those two are added correctly, which respects operator precedence rules.
Your expression without the assignment is equivalent to:
operator+(a.operator++(), operator*(b, b))
The order of evaluation of function arguments is unspecified, so technically, ++a can be evaluated before b * b, but also the other way around.
Can I always consider an overloaded operator as a function (which makes the behavior of x = x++ well-defined when x is an Int)?
Yes and no. If Int does the same thing as the "normal" operators would do, then no (until C++17), because that would be undefined behavior. But if Int doesn't change x in x++ for example, then yes.
As far as I known, the prefix ++ has higher precedence than binary *.
Higher precedence doesn't mean that the prefix increment will be called before the multiplication operator, but in which order the parameters are bound to the corresponding operation.
I understand my second statement that "why & is not needed for normal function pointers" because function name itself is address of the function.
What I do not understand is why '&' is strictly needed for member function pointers?
Examples:
Normal function pointers:
int add(int a, int b) {
return (a + b);
}
int (*fp)(int, int);
fp = add;
(*fp)(2, 3) // This would give me addition of a and b, i.e. 5
Member function pointers:
class ABC {
public:
int i;
ABC() { i = 0; }
int addOne(int j) {
return j + 1;
}
};
// Member function pointer
int (ABC::*mfp)(int);
// This is what I am talking about. '&' in below line.
mfp = &ABC::addOne;
ABC abc;
std::cout << (abc.*mfp)(2) << std::endl;
It seems to me that the address-of operator is necessary for member function pointers because the right-hand side (rhs) of the declaration is a constant rather than a variable.
We wouldn't say
int (ABC::*mfp)(int);
mfp = ABC::addOne();
because that would be to invoke a function.
Furthermore, the scope resolution operator :: has the highest precedence in the C++ table of operator precedence:
https://github.com/MicrosoftDocs/cpp-docs/blob/master/docs/cpp/cpp-built-in-operators-precedence-and-associativity.md
The :: operator is evaluated before any other operators are on the rhs. I suppose the complier wonders "Hmmmm...what's that? That should be a function, but..." and then sees the address-of operator and knows what the developer needs.
Is the following code safe :
#include <iostream>
#include <cstdint>
struct A{
int i = 0;
virtual int foo() {return i;}
};
struct B : A{
int foo() override {return i+2;}
};
using handle_t = std::uintptr_t;
handle_t get(B& a){
return reinterpret_cast<handle_t>(&a);
}
void use(handle_t h){
auto p= reinterpret_cast<A*>(h); //
std::cout << p->foo() << "\n";
}
int main(int argc, char *argv[])
{
B a;
auto h = get(a);
use(h);
return 0;
}
CppReference's page says one can :
reinterpret_cast from B* to std::uintptr_t
reinterpret_cast from std::uintptr_t to B* (because it's the same type back and forth)
reinterpret_cast from B* to A*
So, is it safe to merge the last two ?
I don't know what "safe" means, but the behavior of a program that uses this code is undefined. You can convert a pointer into an integer type that's large enough to hold the value, and you can convert that value back into a pointer with the same type as the original. The code in the question doesn't do that: it converts the value into a pointer with a different type from the original.
In that particular case the code is safe. That's because the safety of upcasting. This is always allowed for public inheritance, without an explicit type cast.
In short you're just force something could be consider as implicit.
The passage of B type's address with a uintptr_t is useless also but allowed because "the same type".
Edit
About uintptr_t.
Integer type capable of holding a value converted from a void pointer and then be converted back to that type with a value that compares equal to the original pointer.
Note "compares equal to the original pointer".
int a=5;
++a=a;
Please find the above code segment. The code is ok for the c++(g++) compiler but shows error while using c (gcc) compiler. May I know the reason for this?
The error in c compiler is "lvalue required as left operand of assignment".
There is operator overloading in C++ (and you can overload pre-increment also), so to achieve some additional goals pre-increment operator returns lvalue in C++.
For example:
Your class may implement some pointer functionality and may need:
pre-increment for pointer shifting;
assignment operator for assignment to pointer value (value by the address).
Pre-increment may be useful in this case.
Abstract code example:
class MyIntPtr {
int *val;
...
public:
MyIntPtr(int *p) { ... };
MyIntPtr &operator++() { ++val; return *this; };
void operator=(int i) { *val = i; }
...
};
...
int array[10];
MyIntPtr ptr(array);
for(int i = 0; i < sizeof array; ++i)
++ptr = i;
Because in C++, the preincrement operator yields an lvalue, whereas in C, it's an rvalue.
I was confused about why can't compare pointers to member using binary operator<
class Point3d{
protected:
//..
public:
float x;
static list<Point3d*> *freeList;
public:
float y;
static const int chunkSize = 250;
public:
float z;
};
and a template:
template< class class_type, class data_type1, class data_type2 >
char* access_order(data_type1 class_type:: *mem1, data_type2 class_type:: *mem2)
{
return
mem1 < mem2 ?
"member 1 accurs first":
"member 2 accurs first";
}
when I called the access_order like below:
access_order(&Point3d::z, &Point3d::y);
the g++ reported:
"invalid operands of types ‘float Point3d::*’ and ‘float Point3d::*’ to binary ‘operator<’"
Is there a way compare pointer to member, I mean the unequal comparison, and how?
You can compare the addresses of the members of an object:
A a;
if (std::less<void*>()(&a.a, &a.b))
std::cout << "a precedes b\n";
else
std::cout << "a follows b\n";
One of the best option - make a raw copy via std::memcpy, calculate hash and then use it for comparison (thanks #HolyBlackCat for comments). The function below calculates the hash for passed pointer-to-member (tested on modern C++ 17 compilers VS, GCC. CLang).
#include <cstring>
#include <string_view>
#include <functional>
template <typename TObject, typename TMember>
size_t HashMemberPtr(TMember TObject::* memberPtr)
{
char buf[sizeof memberPtr];
std::memcpy(&buf, &memberPtr, sizeof memberPtr);
return std::hash<std::string_view>{}(std::string_view(buf, sizeof buf));
}
Unfortunately it's not compatible with std::hash<> as last requires only one template argument.
How to use:
struct CPoint3D
{
float x;
float y;
float z;
};
int main()
{
const size_t xHash = HashMemberPtr(&CPoint3D::x);
assert(xHash == HashMemberPtr(&CPoint3D::x));
assert(xHash != HashMemberPtr(&CPoint3D::y));
assert(xHash != HashMemberPtr(&CPoint3D::z));
return 0;
}
For the same reason you can't compare pointers in general. The only
comparisons for order that are supported is if two data pointers point
into the same array. Otherwise, the results of the comparison are
unspecified; a compiler is not required to make the operators "work" in
any reasonable way. (Ensuring a total order here would require extra
computation on some architectures.) Since there's no case you can get
specified results for a pointer to member, the standard doesn't allow
them as arguments to the operators.
If you need total ordering, std::less et al. is guaranteed to provide
it. Including, if I understand the standard correctly, member pointers.
(Although providing a total ordering for pointer to member functions
would probably be very expensive.) Even then, however, this ordering
may be arbitrary; it would certainly not be required to reflect any
ordering in memory.
If the Point3d overloads < then deference them and do the compare,
return
*mem1 < *mem2 ?
"member 1 accurs first":
"member 2 accurs first";
or change the signature
char* access_order(data_type1 class_type:: &mem1, data_type2 class_type:: &mem2)
char* access_order(data_type1 class_type:: mem1, data_type2 class_type:: mem2)
Did you want to do the compare on the actual memory address?
Pointers to members do not point to some memory themselves. They are just labels. The only thing you can do with them is to convert them to reference to pointee value of the given object with operator .* or ->* or store in another pointer to member variable.
struct A
{
int a;
float b;
};
A a;
int A::* p2m = &A::a;
int A::* p2m2 = p2m;
int & realPointer = a.*p2m;
Note, that you only can compare pointers of the same type, so you can't compare pointer to A::a (int A::*) with pointer to A::b (float A::*)
Pointer comparison seem to tempting everyone but they always lead to a non-portable or undefined behavior.
The easiest answer is that you should never do this. There is no computational problem that cannot be solved with classical approaches.
Don't get me wrong, I am aware that asking wrong questions might get interesting thoughts, or a build better understanding on how a language or even CPU works.