How Does the Ampersand(&) Sign Work in C++

how does the ampersand(&) sign work in c++?

To start, note that

this

is a special pointer ( == memory address) to the class its in.
First, an object is instantiated:

CDummy a;

Next, a pointer is instantiated:

CDummy *b;

Next, the memory address of a is assigned to the pointer b:

b = &a;

Next, the method CDummy::isitme(CDummy ¶m) is called:

b->isitme(a);

A test is evaluated inside this method:

if (¶m == this) // do something

Here's the tricky part. param is an object of type CDummy, but ¶m is the memory address of param. So the memory address of param is tested against another memory address called "this". If you copy the memory address of the object this method is called from into the argument of this method, this will result in true.

This kind of evaluation is usually done when overloading the copy constructor

MyClass& MyClass::operator=(const MyClass &other) {
// if a programmer tries to copy the same object into itself, protect
// from this behavior via this route
if (&other == this) return *this;
else {
// otherwise truly copy other into this
}
}

Also note the usage of *this, where this is being dereferenced. That is, instead of returning the memory address, return the object located at that memory address.

Pointers in C: when to use the ampersand and the asterisk?

You have pointers and values:

int* p; // variable p is pointer to integer type
int i; // integer value

You turn a pointer into a value with *:

int i2 = *p; // integer i2 is assigned with integer value that pointer p is pointing to

You turn a value into a pointer with &:

int* p2 = &i; // pointer p2 will point to the address of integer i

Edit:
In the case of arrays, they are treated very much like pointers. If you think of them as pointers, you'll be using * to get at the values inside of them as explained above, but there is also another, more common way using the [] operator:

int a[2];  // array of integers
int i = *a; // the value of the first element of a
int i2 = a[0]; // another way to get the first element

To get the second element:

int a[2]; // array
int i = *(a + 1); // the value of the second element
int i2 = a[1]; // the value of the second element

So the [] indexing operator is a special form of the * operator, and it works like this:

a[i] == *(a + i);  // these two statements are the same thing

What does '&' do in a C++ declaration?

The "&" denotes a reference instead of a pointer to an object (In your case a constant reference).

The advantage of having a function such as

foo(string const& myname) 

over

foo(string const* myname)

is that in the former case you are guaranteed that myname is non-null, since C++ does not allow NULL references. Since you are passing by reference, the object is not copied, just like if you were passing a pointer.

Your second example:

const string &GetMethodName() { ... }

Would allow you to return a constant reference to, for example, a member variable. This is useful if you do not wish a copy to be returned, and again be guaranteed that the value returned is non-null. As an example, the following allows you direct, read-only access:

class A
{
public:
int bar() const {return someValue;}
//Big, expensive to copy class
}

class B
{
public:
A const& getA() { return mA;}
private:
A mA;
}
void someFunction()
{
B b = B();
//Access A, ability to call const functions on A
//No need to check for null, since reference is guaranteed to be valid.
int value = b.getA().bar();
}

You have to of course be careful to not return invalid references.
Compilers will happily compile the following (depending on your warning level and how you treat warnings)

int const& foo() 
{
int a;

//This is very bad, returning reference to something on the stack. This will
//crash at runtime.
return a;
}

Basically, it is your responsibility to ensure that whatever you are returning a reference to is actually valid.

What does ampersand mean in function call?

Yes, you are right: the ampersand takes the address of an lvalue (a variable) and passes it as pointer.

Your adjust() function would look like:

void adjust(double *a, double f) {
... do a lot of stuff
*a = *a * f/2+1.0; // dummy formula that will change the content
...
};

So in the function you'd use *a every time you'd want to use the value pointed at by the first argument, and everytim you want to assign a new value to the original variable.

What does (&) -- ampersand in parentheses -- mean in this code?

It's shorthand for something like this:

template <typename T, size_t N>
constexpr size_t size_of(T (&anonymous_variable)[N]) {
return N;
}

In the function, you don't actually need the name of the variable, just the template deduction on N - so we can simply choose to omit it. The parentheses are syntactically necessary in order to pass the array in by reference - you can't pass in an array by value.

Meaning of Ampersand(&), in .rc files

Those &s are only for menus, and they define the "menu access keys", or "mnemonics", also called accelerators and shortcuts by others. And yes, the terminology is overloaded and confusing.

More reading: why you probably didn't realize these were there.

What does the & (ampersand) at the end of member function signature mean?

Ref-qualifiers - introduced in C++11

Ref-qualifiers is not C++17 feature (looking at the tag of the question), but was a feature introduced in C++11.

struct Foo
{
void bar() const & { std::cout << "const lvalue Foo\n"; }
void bar() & { std::cout << "lvalue Foo\n"; }
void bar() const && { std::cout << "const rvalue Foo\n"; }
void bar() && { std::cout << "rvalue Foo\n"; }
};

const Foo&& getFoo() { return std::move(Foo()); }

int main()
{
const Foo c_foo;
Foo foo;

c_foo.bar(); // const lvalue Foo
foo.bar(); // lvalue Foo
getFoo().bar(); // [prvalue] const rvalue Foo
Foo().bar(); // [prvalue] rvalue Foo

// xvalues bind to rvalue references, and overload resolution
// favours selecting the rvalue ref-qualifier overloads.
std::move(c_foo).bar(); // [xvalue] const rvalue Foo
std::move(foo).bar(); // [xvalue] rvalue Foo
}

Note that an rvalue may be used to initialize a const lvalue reference (and in so expanding the lifetime of the object identified by the rvalue), meaning that if we remove the rvalue ref-qualifier overloads from the example above, then the rvalue value categories in the example will all favour the remaining const & overload:

struct Foo
{
void bar() const & { std::cout << "const lvalue Foo\n"; }
void bar() & { std::cout << "lvalue Foo\n"; }
};

const Foo&& getFoo() { return std::move(Foo()); }

int main()
{
const Foo c_foo;
Foo foo;

// For all rvalue value categories overload resolution
// now selects the 'const &' overload, as an rvalue may
// be used to initialize a const lvalue reference.
c_foo.bar(); // const lvalue Foo
foo.bar(); // lvalue Foo
getFoo().bar(); // const lvalue Foo
Foo().bar(); // const lvalue Foo
std::move(c_foo).bar(); // const lvalue Foo
std::move(foo).bar(); // const lvalue Foo
}

See e.g. the following blog post for for a brief introduction:

  • Andrzej's C++ blog - Ref-qualifiers

rvalues cannot invoke non-const & overloads

To possibly explain the intent of your recollected quote from the CppCon talk,

"... that the only true way of overloading operator= ..."

we visit [over.match.funcs]/1, /4 & /5 [emphasis mine]:

/1 The subclauses of [over.match.funcs] describe the set of candidate functions and the argument list submitted to overload
resolution in each context in which overload resolution is used. ...

/4 For non-static member functions, the type of the implicit object parameter is

  • (4.1) — “lvalue reference to cv X” for functions declared without a ref-qualifier or with the & ref-qualifier

  • (4.2) — “rvalue reference to cv X” for functions declared with the && ref-qualifier


where X is the class of which the function is a member and cv is the
cv-qualification on the member function declaration. ...

/5 ... For non-static member functions declared without a ref-qualifier, an additional rule applies:

  • (5.1) — even if the implicit object parameter is not const-qualified, an rvalue can be bound to the parameter as long as
    in all other respects the argument can be converted to the type of the
    implicit object parameter.
    [ Note: The fact that such an argument is
    an rvalue does not affect the ranking of implicit conversion
    sequences. — end note ]

From /5 above, the following overload (where the explicit & ref-qualifier has been omitted)

struct test
{
test& operator=(const test&) { return *this }
}

allows assigning values to r-values, e.g.

int main()
{
test t1;
t1 = test(); // assign to l-value
test() = t1; // assign to r-value
}

However, if we explicitly declare the overload with the & ref-qualifier, [over.match.funcs]/5.1 does not apply, and as long we do not supply an overload declared with the && ref-qualifier, r-value assignment will not be allowed.

struct test
{
test& operator=(const test&) & { return *this; }
};

int main()
{
test t1;
t1 = test(); // assign to l-value
test() = t1; // error [clang]: error: no viable overloaded '='
}

I won't place any opinion as to whether explicitly including the & ref-qualifier when declaring custom assignment operator overloads is "the only true way of overload operator=", but would I dare to speculate, then I would guess that the intent behind such a statement is the exclusion of to-r-value assignment.

As a properly designed assignment operator should arguably never be const (const T& operator=(const T&) const & would not make much sense), and as an rvalue may not be used to initialize a non-const lvalue reference, a set of overloads for operator= for a given type T that contain only T& operator=(const T&) & will never proviade a viable overload that can be invoked from a T object identified to be of an rvalue value category.

We use ampersand during user input but Why we don't need this during assignment in C?

The scanf function has to access the object into which a value is being read. All C function arguments are passed by value, so if you wrote

int a, b;
scanf("%d%d", a, b); // THIS IS WRONG

the scanf function would only be able to see the values of a and b as they were when the function was called. It would have no way to modify a.

Assignment requires an lvalue as its left operand. An lvalue (to simplify slightly) is an expression that designates an object; the name of a variable is the most common kind of lvalue. Since the assignment operator = is built into the language, it can have special rules that apply to it. In particular, the lvalue on the LHS of the = identifies the object being assigned to; it doesn't yield the (previous) value of that object. That's how assignment is defined by the language.

For example, if you write:

a = b;

both a and b are lvalues, but they're treated differently because of their context. a refers to the object whose name is a. b also refers to an object, but in that context it yields the current value of that object.

If assignment were implemented as a function call, it would have to take the address of the object being assigned to:

int assign(int *lhs, int rhs);
assign(&a, b);

The language could have required an address for the LHS of an assignment, but allowing just the name of the target object is much more convenient.

(To explain the simplification above, the current standard's definition of lvalue is "an expression (with an object type other than void) that potentially designates an object". The word "potentially" is there because, for example, *ptr where ptr is a pointer is an lvalue even if the current value of ptr is NULL. Being an lvalue is a compile-time concept. Attempting to assign a value to *ptr is legal, but has undefined behavior.)



Related Topics



Leave a reply



Submit