Why this behavior?

Hey guys! I have a struct template nodeType and a class template nodeType as shown below and I have shown the simple definition of the member function nodeType::getInfo():


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
template<class elemType>
struct nodeType                           //structure of the node
{
    elemType info;
    nodeType<elemType> *link;
};


template<class elemType>
class nodeType                       //structure of the node                                                                                                                                                                             
{
public:
	const nodeType<elemType>& operator=(const nodeType<elemType>&);
        void setInfo(const elemType& elem);
	elemType getInfo() const;
	void setLink(nodeType<elemType>* ptr);
	nodeType<elemType>* getLink() const;
	nodeType();
	nodeType(const elemType& elem, nodeType<elemType>* ptr);
	nodeType(const nodeType<elemType>& otherNode);
	~nodeType();
	
private:
	elemType info;
	nodeType<elemType>* link;
};


elemType nodeType<elemType>::getInfo() const
{
	return info;
}



Consider the two member functions of another class (a class template linkedListType<elemType>) as shown below. While the two functions are used in two different programs, they are pretty much the same except for lines 16 and 39 and, more importantly, the first function uses the struct template while the second function uses the class template above.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
const elemType& linkedListType<elemType>::kthElement(int k)
{
	int i = 1; 
	
	if (0 < k && k <= count)
	{	
		nodeType<elemType>* current;
		
		current = first;
		while (i < k)
		{
			current = current->link;
			i++;
		}
		
		return current->info;		
	}
	else
		throw k;		//List does not have k elements!		    
}



const elemType& linkedListType<elemType>::kthElement(int k)
{
	int i = 1; 
	
	if (0 < k && k <= count)
	{	
		nodeType<elemType>* current;
		
		current = first;
		while (i < k)
		{
			current = current->link;
			i++;
		}
		
		return current->getInfo();		
	}
	else
		throw k;		//List does not have k elements!		    
}



My question is:
Why does the first function work fine and the second doesn't??!

I got a compiler error about the second function that states
[Warning] returning reference to temporary


Last edited on
elemType nodeType::elemType getInfo() const;
returns an anonymous temporary object (a prvalue); a copy of the object in the node.
The life-time of the temporary object is limited.

Make it return a reference to const to the object in the node (return an lvalue) :
const elemType& nodeType::elemType getInfo() const;
I cannot change any thing in the specified class definition given in the exercise I am working on! I have control over the linkedListType<elemType> functions though. Is there a way I can work around this?



1
2
// const elemType& linkedListType<elemType>::kthElement(int k) { /* ... */ }
elemType linkedListType<elemType>::kthElement(int k) { /* ... */ }


1
2
3
4
5
6
7
8
9
10
11
elemType linkedListType<elemType>::kthElement( std::size_t k ) // first element at position zero
{
    if( k < count ) // count is number of elements in the list. valid positions are 0 .. count-1
    {
        auto current = first ;
        for( std::size_t pos = 0 ; pos < k ; ++pos ) current = current->link ;
        return current->getInfo();
    }

    else throw std::out_of_range( "linked list: invalid position" ) ; // #include <stdexcept>
}
Last edited on
Thx JLBorges for your coding effort; however, it seems like a trivial solution that just required removing the const reference designation from the function heading. I knew that stripping the function of the const reference specification would solve the problem but, I guess I took a cue from the function prototype for overloading the assignment operator for a class:

const classname& classname::opeator=(const classname&);

It similarly returns a const reference type (*this). While I know this is different from my case, the question I have is:
Is this (i.e. operator=), by and large, the only case where a const reference type is returned by a function?

I would admit I don't think I have fully understood when it is appropriate to return a reference or a const reference from a function! By this I mean how to know exactly when returning just a type, a reference to a type, or a const reference to a type is sufficient/required/mandatory.

Please let me have any relevant/useful links on the subjects of reference and const reference you may know of.
Is this (i.e. operator=), by and large, the only case where a const reference type is returned by a function?

No. Another good example is when returning the data part of a linked list node.... :)
I cannot change any thing in the specified class definition given in the exercise

That is unfortunate because it's a VERY bad design to have getInfo() return a copy of the data. Consider this:
1
2
3
4
5
struct MyInfo {
    char bigArray[1000000];
};

nodeType<MyInfo> node;


Now each and every time you call node.getInfo(), the compiler is forced to return a copy of the data, which 1MB in size, so every getInfo() call results in copying a megabyte of memory. Icky.
> when returning just a type, a reference to a type, or a const reference to a type is sufficient/required/mandatory.

In general, favour value semantics; favour pass by value and returning values.
This is particularly true for object-oriented interfaces.

1
2
3
4
5
6
7
8
struct book 
{
     virtual ~book() = default ;
    
     virtual const std::string& title() const = 0 ; // implementation in disguise
     
     virtual std::string author() const = 0 ; // fine: fully encapsulates implementation
}; 


There are situations where value semantics is impossible; for instance, return an object of a polymorphic type, or pass a non-copyable, non-moveable object.

Sometimes, passing by reference or returning an object (an lvalue reference) is required; for instance the identity of the object is important.

Though this has become less common after C++11, we may want to use references in order to avoid the expensive copy of an object. This is usually true in generic code where the type of the object could be anything.

As far as the const qualifier is concerned, on lvalues, use const pro-actively - if it can be const, make it const.
Avoid const-qualifying prvalues.
Topic archived. No new replies allowed.