namespaces, linkage error, operator overloading

J

jakester

I am using Visual C++ 2007 to build the code below. I keep getting
linkage error. Could someone please tell me what I am doing wrong? The
code works until I start using namespace for my objects.

Error 1 error LNK2019: unresolved external symbol "class
std::basic_ostream<char,struct std::char_traits<char> > & __cdecl
graph::eek:perator<<(class std::basic_ostream<char,struct
std::char_traits<char> > &,class graph::Node &)" (??6graph@@YAAAV?
$basic_ostream@DU?$char_traits@D@std@@@std@@AAV12@AAVNode@0@@Z)
referenced in function _main Program.obj

//Node.h
#ifndef NODE_H
#define NODE_H

#include <string>
#include <iostream>

using std::eek:stream;
using std::string;

namespace graph {
class Node
{
friend ostream &operator<<(ostream &, Node &);
public:
Node(void);
Node(const string id);
~Node(void);

string getId() const;
void setId(const string &);
private:
string id;
};
}

#endif

//Node.cpp
#include "Node.h"

using graph::Node;

namespace graph {
Node::Node(void)
{
}


Node::Node(const string id) {
}

Node::~Node(void)
{
}

string Node::getId() const {
return id;
}

void Node::setId(const string &id) {
this->id = id;
}
}

ostream &operator<<(ostream &output, Node &node) {
output << node.getId();
return output;
}

//Program.cpp
#include "Node.h"

using graph::Node;
using std::cout;
using std::endl;

int main() {
Node a("a");
Node b("b");

cout << a << " and " << b << endl;
}
 
T

twomers

#ifndef NODE_H
#define NODE_H

#include <string>
#include <iostream>

using std::eek:stream;
using std::string;

namespace graph {
class Node
{
public:
friend ostream &operator<<(ostream &, Node &);
Node(void);
Node(const string id);
~Node(void);

string getId() const;
void setId(const string &);
private:
string id;
};

}

#endif

//Node.cpp

using graph::Node;

namespace graph {
Node::Node(void)
{
}

Node::Node(const string id) {
}

Node::~Node(void)
{
}

string Node::getId() const {
return id;
}

void Node::setId(const string &id) {
this->id = id;
}

ostream &operator<<(ostream &output, Node &node) {
output << node.getId();
return output;
}
}



//Program.cpp

using graph::Node;
using std::cout;
using std::endl;

int main() {
Node a("a");
Node b("b");

cout << a << " and " << b << endl;
}

Fixes it.
 
J

jakester

Could you help to explain what happens by declaring the operator<<
inside public as opposed to outside? I am using C++, How to Program by
Deitel/Deitel (3rd edition) as a reference. On page 528, they talk
about implementation of operator overloading as member versus non-
member functions. This is their guidance:

1. (), [], ->, or any assignment operator must be implemented as
member function
2. when an operator function is implemented as a member function, the
leftmost operand must be a class object of the operator's class ... if
the left operand must be an object of a different class or built-in
type, this operator function must be implemented as a non-member
function.

They specifically state that << and >> operators should be implemented
as non-member functions. Doesn't this mean outside public (as I had in
the original post)?

Thanks.
 
G

Gianni Mariani

jakester said:
I am using Visual C++ 2007 to build the code below. I keep getting
linkage error. Could someone please tell me what I am doing wrong? The
code works until I start using namespace for my objects.

In this case, I'm not sure the compiler is totally compliant, however, I
don't think the error is the issue.
Error 1 error LNK2019: unresolved external symbol "class
std::basic_ostream<char,struct std::char_traits<char> > & __cdecl
graph::eek:perator<<(class std::basic_ostream<char,struct
^^^^^^^^^^^^^^^^^^

Notice the graph:
std::char_traits<char> > &,class graph::Node &)" (??6graph@@YAAAV?
$basic_ostream@DU?$char_traits@D@std@@@std@@AAV12@AAVNode@0@@Z)
referenced in function _main Program.obj

//Node.h
#ifndef NODE_H
#define NODE_H

#include <string>
#include <iostream>

using std::eek:stream;
using std::string;

notice, this is outside the graph: - In this case I suggest you don't do
this using in a header.
namespace graph {

class Node
{
friend ostream &operator<<(ostream &, Node &);

OK - friend in this case injects a declaration into the enclosing
namespace, i.e. graph. But, this probably doesn't do what you think you
want it to do since operator<< is a template not a straight function.

BTW - you really want a const Node &
public:
Node(void);
Node(const string id);
~Node(void);

string getId() const;

// don't have to do this - but it's prolly a good idea to return a
// const std::string & here.
void setId(const string &);
private:
string id;
};
}

#endif

//Node.cpp
#include "Node.h"

using graph::Node;

namespace graph {
Node::Node(void)
{
}


Node::Node(const string id) {
}

Node::~Node(void)
{
}

string Node::getId() const {
return id;
}

void Node::setId(const string &id) {
this->id = id;
}
}

This is being defined in the global namespace.
ostream &operator<<(ostream &output, Node &node) {
output << node.getId();
return output;
}

//Program.cpp
#include "Node.h"

using graph::Node;
using std::cout;
using std::endl;

int main() {
Node a("a");
Node b("b");

cout << a << " and " << b << endl;
}


when I compile your code on gcc I get this :

friendop1.cpp: In function 'int main()':
friendop1.cpp:69: error: ambiguous overload for 'operator<<' in
'std::cout << a'
friendop1.cpp:53: note: candidates are: std::eek:stream&
operator<<(std::eek:stream&, graph::Node&)
friendop1.cpp:11: note: std::eek:stream&
graph::eek:perator<<(std::eek:stream&, graph::Node&)

Which is more like what I expect but I don't think that's totally right
either - not sure but it is irrelevant.

The code at the end of this posting shows somthing that does compile and
run on gcc (probably will also on VC).

Some people will object to adding things to the std namespace some
people will say that this is specifically allowed. I've seen some
compilers that won't work too well if it's not in the std:: namespace so
that's why I put it there but it may have been a buggy compiler.


--------------------------------------------------------------
#include <iostream>
#include <string>


namespace graph {
class Node;
}

namespace std {

template<
typename i_char_type,
class i_traits
basic_ostream<i_char_type, i_traits>& operator << (
basic_ostream<i_char_type, i_traits> & i_ostream,
const graph::Node & i_value
);

} // end namespace

namespace graph {
class Node
{
// friend decl
template<
typename i_char_type,
class i_traits
friend
std::basic_ostream<i_char_type, i_traits>& std::eek:perator << (
std::basic_ostream<i_char_type, i_traits> & i_ostream,
const Node & i_value
);

public:
Node(void);
Node(const std::string id);
~Node(void);

const std::string & getId() const;
void setId(const std::string &);
private:
std::string id;
};
}

namespace std {

template<
typename i_char_type,
class i_traits
basic_ostream<i_char_type, i_traits>& operator << (
basic_ostream<i_char_type, i_traits> & i_ostream,
const graph::Node & i_value
) {
i_ostream << i_value.getId();
return i_ostream;
}


} // end namespace


//#endif

//Node.cpp
//#include "Node.h"

using graph::Node;

namespace graph {
Node::Node(void)
{
}


Node::Node(const std::string id) :id(id) {
}

Node::~Node(void)
{
}

const std::string & Node::getId() const {
return id;
}

void Node::setId(const std::string &id) {
this->id = id;
}
}

//Program.cpp
//#include "Node.h"

using graph::Node;
using std::cout;
using std::endl;

int main() {
Node a("a");
Node b("b");

cout << a << " and " << b << endl;
}
 
J

James Kanze

[...]
namespace graph {

[...]
ostream &operator<<(ostream &output, Node &node) {

If this declaration compiles, it's time to change compilers,
fast. It's not, and never has been, legal C++; a member
operator<< takes only one (explicit) parameter.
 
J

James Kanze

[...]
notice, this is outside the graph: - In this case I suggest
you don't do this using in a header.

I agree that I don't like using declarations at global scope,
especially in a header. But I don't think that it has any
relationship with his problem.
OK - friend in this case injects a declaration into the enclosing
namespace, i.e. graph. But, this probably doesn't do what you think you
want it to do since operator<< is a template not a straight function.

His operator<< is not a template. On the other hand, he *has*
effectively declared a function graph::eek:perator<<. When
overload resolution choses this function, it will look for an
implementation in the namespace graph.

[...]
This is being defined in the global namespace.

Which means that it is a different function. And that he still
doesn't have a definition for the friend function in namespace
graph which he declared in the header.
when I compile your code on gcc I get this :
friendop1.cpp: In function 'int main()':
friendop1.cpp:69: error: ambiguous overload for 'operator<<' in
'std::cout << a'
friendop1.cpp:53: note: candidates are: std::eek:stream&
operator<<(std::eek:stream&, graph::Node&)
friendop1.cpp:11: note: std::eek:stream&
graph::eek:perator<<(std::eek:stream&, graph::Node&)
Which is more like what I expect but I don't think that's totally right
either - not sure but it is irrelevant.

Are you sure you compiled the same code? There was no
"::eek:perator<<( std::eek:stream&, graph::Node& )" (the first
candidate g++ mentions above) visible in Program.cpp.

If you put all of the code in a single file, I'm not 100% sure,
but I think g++ would be right. Normal name lookup finds the
global function, and ADL finds the function in the namespace, so
both are in the overload set (along with a lot of other
functions from std, which are eliminated because they can't be
called with the given arguments). In his example, he had two
source files; graph::eek:perator<< was the only version visible in
the header.

Note that the name of the friend function graph::eek:perator<<
isn't actually directly visible in namespace graph; friend name
injection was removed by the standards committee. But the
function will be found by ADL. If that's the only function that
the compiler sees (case in the original code), then that's what
overload resolution will choose, and since he's not implemented
it, he gets the error message he saw. If there is also another
function, with the exact same paramters, then the call is
ambiguous.
The code at the end of this posting shows somthing that does compile and
run on gcc (probably will also on VC).
Some people will object to adding things to the std namespace some
people will say that this is specifically allowed.

It depends on what you add.
I've seen some
compilers that won't work too well if it's not in the std:: namespace so
that's why I put it there but it may have been a buggy compiler.
namespace graph {
class Node;
}
namespace std {

template<
typename i_char_type,
class i_traits
basic_ostream<i_char_type, i_traits>& operator << (
basic_ostream<i_char_type, i_traits> & i_ostream,
const graph::Node & i_value
);

That's illegal, I think. About the only thing your allowed to
add are specializations of existing templates, not new
templates. But why be complicated? If he defines operator<< in
the namespace graph, in his .cpp, everything should be just
fine.

--
 
J

James Kanze

Could you help to explain what happens by declaring the operator<<
inside public as opposed to outside?

First, ignore my previous response to twomers posting; I misread
namespace for class. (It's late here.)
I am using C++, How to Program by
Deitel/Deitel (3rd edition) as a reference. On page 528, they talk
about implementation of operator overloading as member versus non-
member functions. This is their guidance:
1. (), [], ->, or any assignment operator must be implemented as
member function
2. when an operator function is implemented as a member function, the
leftmost operand must be a class object of the operator's class ... if
the left operand must be an object of a different class or built-in
type, this operator function must be implemented as a non-member
function.
They specifically state that << and >> operators should be
implemented as non-member functions. Doesn't this mean
outside public (as I had in the original post)?

Non-member means that they are not members of the class. (A
friend declaration does not declare a member of the class.)
They still have to be defined in the correct namespace. Two
functions with the same names in different namespaces are
different functions.
 
G

Gianni Mariani

James Kanze wrote:

....
That's illegal, I think.

In my recollection I see 50/50 with responses on that one with no one
truly convinced that it is legal or illegal. Since you can't
"specialize" a function (like you can a class), however, specialization
is accomplished by overlaoding a function. For all intents and
purposes, overloading is specialization for functions. So, was/is it
the intent of the standard to not allow overloading ? I don't believe
I've come across a definitive answer.
... About the only thing your allowed to
add are specializations of existing templates, not new
templates. But why be complicated? If he defines operator<< in
the namespace graph, in his .cpp, everything should be just
fine.

You're probably right. I remember having problems overloading
operator<< in different namespaces in the past and what I did above
solved the problem. I more than likely was a buggy compiler that didn't
handle argument dependent lookup and the work-around is no longer needed.
 
J

James Kanze

James Kanze wrote:
In my recollection I see 50/50 with responses on that one with no one
truly convinced that it is legal or illegal.

The standard says it's illegal; there's no doubt about that. In
practice, however, I think the probability of it causing
problems is about 0.
Since you can't "specialize" a function (like you can a
class), however, specialization is accomplished by overlaoding
a function.

The standard is clear: a program may add template
specializations to namespace std. Nothing about overloads.
Specialization and overloading are two very different things.
For all intents and
purposes, overloading is specialization for functions.
From where do you get that? For some very specific purposes,
specialization and overloading play similar rules. But in
general, they are two very different mechanisms, with different
rules accross the board.
So, was/is it
the intent of the standard to not allow overloading ? I don't believe
I've come across a definitive answer.

I'd say that the actual words in the standar are a definitive
answer. If you don't think that that they express the actual
intent, a defect report would be in order; to date, there isn't
one concerning this point, which means that it seems clear, and
means what it seems to mean.
You're probably right. I remember having problems overloading
operator<< in different namespaces in the past and what I did above
solved the problem.

And I confused namespace and class in my first reading of his
problem. One gets an idea in one's head, and one doesn't see
what is actually written. I think it's human nature; at least,
I've never met anyone who was immune to it.
 
G

Gianni Mariani

I'm saying (or intending on saying) what you're ...

.... For some very specific purposes,
specialization and overloading play similar rules.
.... saying here.

.... But in
general, they are two very different mechanisms, with different
rules accross the board.
Yes. That's the point.


I'd say that the actual words in the standar are a definitive
answer. If you don't think that that they express the actual
intent, a defect report would be in order; to date, there isn't
one concerning this point, which means that it seems clear, and
means what it seems to mean.

Ok - it probably needs to be expressed as a defect report because of the
confusion. I also don't see there ever being a problem to overload into
the std:: namespace as a way of extending support for user types.
 
J

jakester

[...]
namespace graph {
[...]

ostream &operator<<(ostream &output, Node &node) {

If this declaration compiles, it's time to change compilers,
fast. It's not, and never has been, legal C++; a member
operator<< takes only one (explicit) parameter.
output << node.getId();
return output;
}
}

--
James Kanze (Gabi Software) email: (e-mail address removed)
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

In my reference book, they say something similar to what you are
saying too (overloading operators as member function should not have 2
operands). I am quite confused. But the code does compile with VC++
and g++. This observation adds much to my confusion.
 
J

James Kanze

jakester said:

This is what I misread.
[...]
ostream &operator<<(ostream &output, Node &node) {
If this declaration compiles, it's time to change compilers,
fast. It's not, and never has been, legal C++; a member
operator<< takes only one (explicit) parameter.
output << node.getId();
return output;
}
}
In my reference book, they say something similar to what you are
saying too (overloading operators as member function should not have 2
operands). I am quite confused. But the code does compile with VC++
and g++. This observation adds much to my confusion.

The problem is simply that I misread the code, and took a
namespace for a class. Overloaded binary operators take one
operand if a member, two if a free function. In namespace
scope, they are free functions, so two operands are in order.

I'm afraid my posting just added to the confusion. Namespaces
and classes have nothing to do with one another.

With regards to your original code: a friend declaration does
*not* declare a member, but rather a function in the enclosing
namespace. Which means, of course, that you have to define the
function in the enclosing namespace; otherwise, you're defining
some other function.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,578
Members
45,052
Latest member
LucyCarper

Latest Threads

Top