0

I have a function, that returns a reference to an entry in a std::vector or a null object. I bind this object to a const & expecting it to point to the original object in the vector:

inline MyClass const & getSomeClassObject( char const * const strName ) const
{
  static MyClass nullObj;
  for ( auto const & entry : m_myClassObjectsVector )
  {
     if ( entry.name == strName ) return entry;
  }

  return nullObj;
}

And this is how the function is called:

MyClass const & myReference = !someOtherObject.isValid() ? m_SomeMemberObject->getSomeClassObject( strName ) : MyClass();

However adding a copy constructor to MyClass showed that the object is copied on return which causes problems because the temporary lives shorter than m_SomeMemberObject.

Apparently the conditional operator has something to do with it as removing it will eliminate the copy.

But can someone explain why this happens?

cigien
  • 57,834
  • 11
  • 73
  • 112
Desperado17
  • 835
  • 6
  • 12
  • 3
    Please show a [mcve]. Something possible wrong is with m_myClassObjectsVector. – 273K Aug 19 '21 at 18:08
  • The returned type of condition operator is same for both sides. I presume the rules mandate the returned type to be `MyClass` and not a reference to it as otherwise on false condition the default constructed instance would get destroyed by the end the of the line - and const reference returned would be faulty. – ALX23z Aug 19 '21 at 18:12
  • 3
    `? :` returns a prvalue in this case (because the last operand is prvalue), forcing the second operand to be copied (to form a prvalue). Unsure how to fix this. – HolyBlackCat Aug 19 '21 at 18:21
  • adding a copy constructor has some implicit actions associated with it. also did you turn on optimizations? what happens when you explicitly work with pointers? – Abel Aug 19 '21 at 18:21
  • 1
    @HolyBlackCat Maybe like `? m_SomeMemberObject->getSomeClassObject( strName ) : m_SomeMemberObject->getNullClassObject();` – 273K Aug 19 '21 at 18:30
  • 1
    @S.M. Yes, that would work. Though I have a distaste for null singletons. – HolyBlackCat Aug 19 '21 at 18:31
  • `... : static_cast(MyClass());` A ternary operator (expressed in very sloppy terms) attempts to find a type to which both alternative operands can be converted. It cannot make an L-value out of an R-value, but it can do the opposite. This question is, to some extent, [a duplicate of this one](https://stackoverflow.com/questions/8535226/return-type-of-ternary-conditional-operator). The answers contain a detailed explanation of the phenomenon. So your L-value operand, if picked by the condition, must become an R-value and making a copy is a way to achieve that. – Andrej Podzimek Aug 19 '21 at 19:24

2 Answers2

0

As discussed in the comments, the possible solution

inline MyClass const & getNullClassObject() const
{
  static MyClass nullObj;
  return nullObj;
}

inline MyClass const & getSomeClassObject( char const * const strName ) const
{
  for ( auto const & entry : m_myClassObjectsVector )
  {
     if ( entry.name == strName ) return entry;
  }

  return getNullClassObject();
}

MyClass const & myReference = !someOtherObject.isValid() ? m_SomeMemberObject->getSomeClassObject( strName ) : m_SomeMemberObject->getNullClassObject();

Both parts of the ? expression now return const lvalue references and no copy for getting pure rvalue performed.

273K
  • 29,503
  • 10
  • 41
  • 64
  • Now that you mention it: would replacing : MyClass(); by a static object also be formally correct? – Desperado17 Aug 19 '21 at 19:06
  • Can you name me the point in the standard where this rule that the last two operands are harmonized stands? – Desperado17 Aug 19 '21 at 19:14
  • 1
    [Conditional operator](https://en.cppreference.com/w/cpp/language/operator_other#Conditional_operator) – 273K Aug 19 '21 at 20:13
0

Since the getSomeClassObject() method returns the same type of object that the calling code wants to use when the strName is not found, I would probably just apply the ?: operator to the parameter of the method call, rather than using it to decide whether to call the method at all, eg:

inline MyClass const & getSomeClassObject( char const * const strName ) const
{
    static MyClass nullObj;
    if (strName) {
        for ( auto const & entry : m_myClassObjectsVector ) {
            if ( strcmp(entry.name, strName) == 0 ) return entry;
        }
    }
    return nullObj;
}
MyClass const & myReference = m_SomeMemberObject->getSomeClassObject( !someOtherObject.isValid() ? strName : nullptr );
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770