I\'m trying to build a program whose source I downloaded from the internet. When I try to compile it, I get the error message
friend declaration specifying
It took a while until I got it but finally I found out.
Thereby, I ignore the mis-leading title (which has already been answered by user463035818 IMHO sufficiently) and concentrate on
friend declaration specifying a default argument must be the only declaration
which raised my attention.
Therefore, I don't repeat what has been said about friend
and access to public members because I think this error deals with a different issue.
First I tried example of OP on coliru (with a little fix and a main()
to test):
#include <iostream>
typedef int Var;
struct Lit {
int x;
// Use this as a constructor:
friend Lit mkLit(Var var, bool sign = false);
bool operator == (Lit p) const { return x == p.x; }
bool operator != (Lit p) const { return x != p.x; }
bool operator < (Lit p) const { return x < p.x; }
};
inline Lit mkLit(Var var, bool sign) { Lit p; p.x = var + var + (int)sign; return p; }
int main()
{
Lit lit2 = mkLit(123, false);
std::cout << "lit2.x: " << lit2.x << '\n';
return 0;
}
Output:
g++ (GCC) 8.1.0
Copyright (C) 2018 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
lit2.x: 246
Live Demo on coliru
Ah, yepp. Runs fine.
Then I did the same on wandbox this time with clang HEAD 8.0.0
:
Start
prog.cc:7:16: error: friend declaration specifying a default argument must be a definition
friend Lit mkLit(Var var, bool sign = false);
^
prog.cc:12:14: error: friend declaration specifying a default argument must be the only declaration
inline Lit mkLit(Var var, bool sign) { Lit p; p.x = var + var + (int)sign; return p; }
^
prog.cc:7:16: note: previous declaration is here
friend Lit mkLit(Var var, bool sign = false);
^
2 errors generated.
1
Finish
Live Demo on wandbox
Here we are.
So, I tried to understand what clang
complains about.
Finally, I found that clang
finds Lit Lit::mkLit()
and this does not match Lit mkLit()
defined later.
This can be fixed by a prototype for mkLit()
inserted before struct Lit
:
typedef int Var;
Lit mkLit(Var var, bool sign = false);
struct Lit {
int x;
// Use this as a constructor:
friend Lit mkLit(Var var, bool sign);
bool operator == (Lit p) const { return x == p.x; }
bool operator != (Lit p) const { return x != p.x; }
bool operator < (Lit p) const { return x < p.x; }
};
Now, I get a new issue: Lit
not (yet) known when proto defined for mkLit()
.
So, I need a forward declaration for Lit
and end up with:
#include <iostream>
typedef int Var;
struct Lit;
Lit mkLit(Var var, bool sign = false);
struct Lit {
int x;
// Use this as a constructor:
friend Lit mkLit(Var var, bool sign);
bool operator == (Lit p) const { return x == p.x; }
bool operator != (Lit p) const { return x != p.x; }
bool operator < (Lit p) const { return x < p.x; }
};
inline Lit mkLit(Var var, bool sign) { Lit p; p.x = var + var + (int)sign; return p; }
int main()
{
Lit lit2 = mkLit(123, false);
std::cout << "lit2.x: " << lit2.x << '\n';
return 0;
}
Output:
Start
lit2.x: 246
0
Finish
Live Demo on wandbox
Problem fixed.
I must admit that I'm not sure whether g++
accepts the original version although it shouldn't or whether clang
denies the original version although it shouldn't. My stomache feeling tends to the former (i.e. clang
is correct)...
After thinking again what happens in g++
, I came to the conclusion that it does the following:
accept the friend Lit Lit::mkLit()
(which is never used)
defining another Lit mkLit()
.
To find out I "converted" struct Lit
to class Lit
and this brings an error in g++
as well:
#include <iostream>
typedef int Var;
class Lit {
int x;
// Use this as a constructor:
friend Lit mkLit(Var var, bool sign = false);
bool operator == (Lit p) const { return x == p.x; }
bool operator != (Lit p) const { return x != p.x; }
bool operator < (Lit p) const { return x < p.x; }
};
inline Lit mkLit(Var var, bool sign) { Lit p; p.x = var + var + (int)sign; return p; }
int main()
{
Lit lit2 = mkLit(123, false);
std::cout << "lit2.x: " << lit2.x << '\n';
return 0;
}
Output:
g++ (GCC) 8.1.0
Copyright (C) 2018 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
main.cpp: In function 'int main()':
main.cpp:17:35: error: 'int Lit::x' is private within this context
std::cout << "lit2.x: " << lit2.x << '\n';
Live Demo on coliru
So, the original version of OP just worked in g++
because struct Lit
with public int Lit::x
simply doesn't need the friend Lit::mkLit()
.
Now, I'm a bit puzzled. Which one is right g++
or clang
? I don't know.
But more basically, I don't understand why a struct needs a friend function, since its members are public anyway.
This is a misunderstanding. There are no structs and classes in C++, but C++ only has classes that can be declared with one of the keywords struct
or class
. The only difference is the default access, ie the following two are identical (apart from the order of their members, which matters if you take their address):
struct foo : private bar {
int x;
private:
int y;
};
And the same with class
:
class foo : bar {
int y;
public:
int x;
};
Using class
or struct
to declare a class is purely a matter of convention. Hence, your question translates to "Why would a class need a friend function?" and the answer is: To allow the friend to access private fields.
The question you linked is about defining the friend function inline vs just declaring it, ie
struct foo {
friend void foofriend() { /*put implementation here*/ }
};
vs
struct foo {
friend void foofriend();
};
void foofriend() { /*put implementation here*/ }
This is indeed related to ADL (tbh I also could not explain it) and is kind of orthogonal to the question what friends are good for.
As many have mentioned before, a struct
is the same as a class
, except that its members are public
by default. It can have private members.
I'm guessing here, but I think the idea behind making this a friend factory function is that it the designer of the library is reserving the right to initialize any private members added in the future.
To answer your questions:
mkLit
before the struct
is a problem because it returns a Lit
, and Lit
hasn't been defined yet (since it now appears below mkLit
).friend
declaration inside the struct
will work because the struct
has no private members -- for now. If you ever merge in a newer version of the original library that changes adds private members (or makes members private by default by changing the text struct
to class
), then it will stop working.(note: I pasted your code into http://godbolt.org and tried the various permutations, using different compilers)