2

When I call a constructor which assigns a passed const & to a const & member variable, what happens? Since a const ref, my understanding is 'very little' - no copies, moves, constructors called, etc - just the copying of something likely to turn out to be a pointer.

E.g.

class ClassA
{
public:
    ClassA(const double a):a_(a){}
    const double a_;
};

class ClassB
{
    const ClassA &classRef_;
public:
    ClassB(const ClassA& a):classRef_(a){}
};

int main()
{
    ClassA aObj(5.212);
    ClassB bObj(aObj);
}

In particular, if I want to declare functions (such as here, the constructor) of ClassB as noexcept, what (if anything )do I need to know about ClassA?

chrisb2244
  • 2,940
  • 22
  • 44
  • Yes, no copies, moves, constructors called, you could declare `noexcept`. What are you worried about? – songyuanyao Jul 29 '15 at 03:49
  • Just wanted to be sure - in my particular case, `ClassA` has `delete`d {move,copy} {assignment operators,constructors}, and I didn't know what potential restrictions I might hit depending on the states of objects, etc - the fact it compiles isn't so relevant, given you can declare all sorts `noexcept` even if it will plainly throw. – chrisb2244 Jul 29 '15 at 03:56

2 Answers2

2

In

int i;
int &r = i;

r = i is not an assignment, it is an initialisation. A reference act like an "alias": r will act like i.

You cannot assign references, only their referent:

r = 2; // same as i = 2

Here:

class ClassB
{
    const ClassA &classRef_;
public:
    ClassB(const ClassA& a):classRef_(a){}
};

In the constructor, the expression a designates a ClassA object (with a constant lvalue).

:classRef_(a) means "initialise the member reference classRef_ so that it becomes an alias for the object designated by a".

The new reference classRef_ will refer to the same object as the a reference. There is no copying.

curiousguy
  • 8,038
  • 2
  • 40
  • 58
0

I believe below should be noexcept safe on the assignment; I don't believe you can guarantee the reference is still "valid" after assignment, but the assignment should be safe. You may want to use a weak_ptr here if you're concerned about the reference being invalidated. The member initialization is a reference assignment (or copy depending on how you look at it). If the reference is borked, you can still copy the reference.

class ClassB
{
    const ClassA &classRef_;
public:
    ClassB(const ClassA& a):classRef_(a){}
};

In order to make the below noexcept, you should make the copy constructor nothrow as well; probably use std::swap(a,b):

class ClassA
{
public:
    ClassA(const double a):a_(a){}
    const double a_;
};
Mark
  • 981
  • 2
  • 13
  • 25
  • 1) Reference are not assigned, only initialised. 2) `weak_ptr` is a compagnon of `shared_ptr`. You don't have a `shared_ptr` here. 3) I am not sure what you want to do with `std::swap`. – curiousguy Jul 29 '15 at 03:48
  • 1) Semantics, fair enough. 2) The reference initialization will allllllllllways succeed, but that doesn't mean the reference is "valid". It could be initialized with a pointer that then gets deleted. Basically trying to make the OP aware that because the reference initialization will ALWAYS succeed, it doesn't mean the resulting reference is valid. 3) Using std::swap is sort of the standard idiom to writing noexcept copy constructors as is the case in ClassA. double is a primitive so it will always work, but if double were a more complex object you would/should take steps to enforce noexcept. – Mark Jul 29 '15 at 04:16
  • 3) But there is no guaranty that `std::swap` on arbitrary types will not throw. – curiousguy Jul 29 '15 at 04:23
  • You're correct. In C++11 its up to the user to define their own std::swap method for an arbitrary type with a strong suggestion that it should be noexcept'd. The convention I'm aware of is that std::swap is: noexcept( std::is_nothrow_move_constructible::value && std::is_nothrow_move_assignable::value ) – Mark Jul 29 '15 at 04:36
  • Reading that, you're saying that a solution is to either a) define a `swap` function which is declared `noexcept`, or b) declare/define both the move constructor and move assignment operator to be `noexcept`, and in case b), using `std::swap(madeUpClass &a, madeUpClass &b)` will utilize `move` rather than `copy` operations, thus requiring no specific `swap` implementation for the class `madeUpClass`? (e.g. `madeUpClass::swap(madeUpClass &rhs)`, or `friend void swap(madeUpClass& lhs, madeUpClass& rhs)`) – chrisb2244 Jul 29 '15 at 04:48
  • Yep, you may use the templated std::swap(T& a, T& b) if T has the copy and move defined. Otherwise you can declare your own std::swap(ExplicitClass& a, ExplicitClass& b). – Mark Jul 29 '15 at 04:52
  • Maybe typo? I suspect my own declaration wouldn't fall in namespace std. Indeed, I think the typical simple implementation involves a statement like `using std::swap;` followed by only non-namespace-specified calls to `swap`? – chrisb2244 Jul 29 '15 at 04:53
  • Correct. "std::swap may be specialized in namespace std for user-defined types, but such specializations are not found by ADL (the namespace std is not the associated namespace for the user-defined type). The expected way to make a user-defined type swappable is to provide a non-member function swap in the same namespace as the type: see Swappable for details. " http://en.cppreference.com/w/cpp/algorithm/swap http://en.cppreference.com/w/cpp/language/extending_std – Mark Aug 05 '15 at 00:45