How to fix a -Wsubobject-linkage warning? - c++

I'm getting a gcc warning for code which compiles fine and warning free in clang and VC++, so I assume it's something gcc specific. This is the code:
namespace myns {
using TokenList = std::vector<size_t>;
using RuleList = std::vector<size_t>;
using RulePathPair = std::pair<size_t, TokenList>;
using CandidatesCollection = struct { std::map<size_t, TokenList> tokens; std::set<RulePathPair> rules; };
class A {
private:
CandidatesCollection _candidates;
};
} // namespace myns
and the warning is:
warning: 'myns::A' has a field 'myns::A::_candidates' whose type has no linkage [-Wsubobject-linkage]
What does this mean and how to get rid of the warning?

I believe the compiler might be wrong here: the type referred to by CandidatesCollection should in fact have external linkage.
[basic.link]/4 ...A name having namespace scope that has not been given internal linkage above has the same linkage as the enclosing namespace if it is the name of
...
(4.3) — a named class (Clause 9), or an unnamed class defined in a typedef declaration in which the class has the typedef name for linkage purposes (7.1.3); ...
[dcl.typedef]/9 If the typedef declaration defines an unnamed class (or enum), the first typedef-name declared by the declaration to be that class type (or enum type) is used to denote the class type (or enum type) for linkage purposes only (3.5). [ Example:
typedef struct { } *ps, S; // S is the class name for linkage purposes
—end example ]
Thus, if CandidatesCollection were defined as
typedef struct { ... } CandidatesCollection;
these two passages make it clear that the class named by CandidatesCollection would happily have external linkage.
Then there's
[dcl.typedef]/2 A typedef-name can also be introduced by an alias-declaration. The identifier following the using keyword becomes a typedef-name and the optional attribute-specifier-seq following the identifier appertains to that typedef-name. It has the same semantics as if it were introduced by the typedef specifier.
Emphasis mine. This suggests that the name introduced by using should give the unnamed class "the name for linkage purposes" just as well as the equivalent typedef declaration, thereby ensuring that the class has external linkage.

Related

Defining sub-structure of sub-class inside parent class

Consider the following snippet of code:
class MyClass
{
private:
struct PrivateClass
{
struct SubStruct;
};
public:
struct PrivateClass::SubStruct {};
private:
PrivateClass::SubStruct member;
};
MSVC and gcc compile this code without any errors. clang, however, produces the following error:
<source>:10:26: error: non-friend class member 'SubStruct' cannot have a qualified name
struct PrivateClass::SubStruct {};
So, who's right? Is this a clang bug?
According to [class]/11 in the C++ 17 Standard:
If a class-head-name contains a nested-name-specifier, the
class-specifier shall refer to a class that was previously declared
directly in the class or namespace to which the nested-name-specifier
refers, or in an element of the inline namespace set (10.3.1) of that
namespace (i.e., not merely inherited or introduced by a
using-declaration), and the class-specifier shall appear in a
namespace enclosing the previous declaration. In such cases, the
nested-name-specifier of the class-head-name of the definition shall
not begin with a decltype-specifier.
So it seems the clang compiler is right.
Standard (latest draft) says:
[class.nest]
If class X is defined in a namespace scope, a nested class Y may be declared in class X and later defined in the definition of class X or be later defined in a namespace scope enclosing the definition of class X.
In this example, the class SubStruct that is declared in the class PrivateClass is defined in neither later in PrivateClass nor later in a namespace scope (but rather, later in a class scope of the outer MyClass). Nor is PrivateClass itself defined in a namespace scope.
Unless there is another rule allowing this, the definition of the sub-nested class as in the example is not at least explicitly allowed. Clang seems to be correct.

Same name in typedef and using from a namespace

Sample code:
struct X { void f() {} };
typedef X A;
namespace N {
struct A { void g() {} };
};
using N::A;
int main()
{
A a;
a.f();
}
This code compiles correctly, and A a; creates an X, not a N::A.
What rule in the standard covers this behaviour? I was expecting an error that A a; would be ambiguous. If I name the first struct A instead of X and remove the typedef, then I do get such an error. (g++ 8.3)
According to [namespace.udecl]/1 using N::A introduces the unqualified A into the declarative region in which the using declaration appears.
... the unqualified-id is declared in the declarative region in which the using-declaration appears as a synonym for each declaration introduced by the using-declarator.
So the ambiguity is covered jointly by [namespace.udecl]/13
Since a using-declaration is a declaration, the restrictions on declarations of the same name in the same declarative region also apply to using-declarations.
and [basic.scope.declarative]/4
Given a set of declarations in a single declarative region, each of
which specifies the same unqualified name,
they shall all refer to the same entity, or all refer to functions and function templates; or
exactly one declaration shall declare a class name or enumeration name that is not a typedef name and the other declarations shall all refer to the same variable, non-static data member, or enumerator, or all refer to functions and function templates; in this case the class name or enumeration name is hidden.
The typedef declaration and the using declaration in the OP don't fill either bullet, so the pair of declarations in the same declarative region is ill-formed.

Example of entity declared in a anonymous namespace that has external linkage

Given the statements below (emphasis mine) in §3.5/4 and in the Note [94] in §7.3.1.1/1, I'd like to have one single example of an entity declared in a unnamed namespace that has external linkage.
§3.5/4
An unnamed namespace or a namespace declared directly or indirectly
within an unnamed namespace has internal linkage. All other namespaces
have external linkage. A name having namespace scope that has not been
given internal linkage above has the same linkage as the enclosing
namespace if it is the name of
a variable; or
a function; or
a named class (Clause 9), or an unnamed class defined in a typedef declaration in which the class has the typedef name for linkage
purposes (7.1.3); or
a named enumeration (7.2), or an unnamed enumeration defined in a typedef declaration in which the enumeration has the typedef name for
linkage purposes (7.1.3); or
an enumerator belonging to an enumeration with linkage; or
a template.
Note [94] on §7.3.1.1/1:
Although entities in an unnamed namespace might have external linkage,
they are effectively qualified by a name unique to their translation
unit and therefore can never be seen from any other translation unit.
You are looking at a defect in the standard.
The change that makes unnamed namespace members have internal linkage happened fairly late in the C++11 standardization process, in November 2010 (CWG issue 1113). As a result, a number of places in the standard needs to be changed, but weren't. One of which is the footnote you quoted.
CWG issue 1603, currently in "ready" status (read: the resolution is likely to be adopted at the next committee meeting), will fix this and a number of other issues related to giving internal linkage to unnamed namespace members.
It's a good question because it's difficult to demonstrate. We can take advantage of other rules in the C++ Standard to show that a variable in an anonymous namespace can have external linkage.
Templating on an int* with external linkage will succeed while templating on an int* with internal linkage will fail.
#include <iostream>
namespace {
// not externally linked, won't compile
// const int i = 5;
// external linkage, compiles
extern int i;
int i = 5;
}
template<int* int_ptr>
struct temp_on_extern_linked_int {
temp_on_extern_linked_int() {
std::cout << *int_ptr << std::endl;
}
};
int main() {
temp_on_extern_linked_int<&i>();
}
As shown the program compiles and runs.
$ g++-4.8 main.cpp -o main
$ ./main
5
Uncommenting the other definition of i causes the compile to fail.
$ g++-4.8 main.cpp -o main
main.cpp: In function 'int main()':
main.cpp:17:30: error: '& {anonymous}::i' is not a valid template argument of
type 'int*' because '{anonymous}::i' does not have external linkage
temp_on_extern_linked_int<&i>();
^
The compiler is quite helpful. It explicitly states that because i doesn't have external linkage the compile failed.
The commented definition of i has internal linkage because it is qualified const without extern. (§3.4.6)
Variables at namespace scope that are declared const and not extern have internal linkage.
Part of the trick is not compiling as C++11.
Why did C++03 require template parameters to have external linkage?
For example
#include <iostream>
namespace
{
extern int x = 10;
void f( int y )
{
extern int x;
std::cout << x + y << std::endl;
}
}
int main()
{
int y = 15;
f( y );
return 0;
}
According to the C++ Standard
6 The name of a function declared in block scope and the name of a
variable declared by a block scope extern declaration have linkage. If
there is a visible declaration of an entity with linkage having the
same name and type, ignoring entities declared outside the innermost
enclosing namespace scope, the block scope declaration declares that
same entity and receives the linkage of the previous declaration. If
there is more than one such matching entity, the program is
ill-formed. Otherwise, if no matching entity is found, the block scope
entity receives external linkage

Typedef-name conflicts with struct tag in C++

This is my second investigation about structure declaration in C++. (The first is here) But now I came across this post. Specifically I am not sure why this is perfectly fine in C but not in C++.
typedef struct{
int one;
int two;
}myStruct;
struct myStruct; //forward declaration fails
void blah(myStruct* pStruct);
The code above compiles fine on my Ubuntu box with GCC. I reason that it is because the first myStruct lives in the normal namespace where function, variable names live. The second myStruct lives in the Tag namespace. When compiler sees myStruct* in the function prototype, it searches in both namespaces and found myStruct in the normal namspace and that name happen to be a typedef name, so it can be a valid type specifier. The second myStruct can be defined later as whatever the programmer wants to be. There won't be any confusion/collision with the first unnamed myStruct since the programmer has to use struct myStruct to refer to the second one.
But in C++, according to the discussion found in the linked question, my understanding is that the first typedef myStruct lives in the normal namespace as usual. The second myStruct also lives in
the normal namespace(no specific tag namespace in C++?) but can be overshadowed by other identifiers. So my question is why wouldn't the first myStruct which is in the same namespace as the second myStruct shadow the second myStruct?
In a more general sense, other than explicit namespaces introduced by the programmer using the namespace facility provided by the C++ language, are there any pre-defined namespaces disambiguating the use of identifiers (including tags, labels, typedef names, object/functino identifiers) like in C? (C has 4 namespaces pre-defined found in my first investigation). Can I find these in the C++ standard stating where these names belong?
EDIT: It seems I didn't ask the question clear enough. All I want to know is
1) Which namespaces (if there are such defined in the language) do Lables, typedef names, tag names of struct/union/enum, normal function, normal variable/object name belong? (If I missed any other kinds of name, please add.)
2) Why can normal function name, normal variable name shadow tag names, while tag names can NOT.
3) If there is any clauses in C++ that specify the name spaces like in C (Section 6.2.1)
In C++, you cannot use struct myStruct to refer to a tagless structure which has been typedefed. And you cannot define a different struct myStruct, because the name collides with the typedef name.
If you add the tag, then both struct myStruct and myStruct alone will refer to the type, in both C and C++:
typedef struct myStruct {
int one;
int two;
} myStruct;
Here there is no collision in C++ because the name resolves to just one type, and this is specifically allowed by a special rule. C++ Standard section 7.1.3 includes the following rules:
In a given non-class scope, a typedef specifier can be used to redefine the name of any type declared in that scope to refer to the type to which it already refers.
If a typedef specifier is used to redefine in a given scope an entity that can be referenced using an elaborated-type-specifier, the entity can continue to be referenced by an elaborated-type-specifier or as an enumeration or class name in an enumeration or class definition respectively.
In a given scope, a typedef specifier shall not be used to redefine the name of any type declared in that scope to refer to a different type.
Similarly, in a given scope, a class or enumeration shall not be declared with the same name as a typedef-name that is declared in that scope and refers to a type other than the class or enumeration itself.
[ Note: A typedef-name that names a class type, or a cv-qualified version thereof, is also a class-name (9.1). If a typedef-name is used to identify the subject of an elaborated-type-specifier (7.1.6.3), a class definition (Clause 9), a constructor declaration (12.1), or a destructor declaration (12.4), the program is ill-formed.
— end note ]

Internal type as a template argument

ISO 98/03 standard (section 14.3.1) seems to forbid using a type with internal linkage as a template parameter. (See example below.) The C++11 standard does not.
G++ - using the old standard - is allowing it.
Am I misreading the 03 standard, or is g++ just letting this slide?
namespace
{
struct hidden { };
}
template<typename T>
struct S
{
T t;
};
int main()
{
S<hidden> s;
return 0;
}
You're correct that C++03 doesn't allow using a type with internal linkage as a template type parameter, while C++11 does.
I seem to recall, however, that definitions inside the anonymous namespace still have external linkage.
Yup, section 3.5 [basic.link] says
A name having namespace scope (3.3.5) has internal linkage if it is the name of
an object, reference, function or function template that is explicitly declared static or,
an object or reference that is explicitly declared const and neither explicitly declared extern nor previously declared to have external linkage; or
a data member of an anonymous union.
A name having namespace scope has external linkage if it is the name of
an object or reference, unless it has internal linkage; or
a function, unless it has internal linkage; or
a named class (clause 9), or an unnamed class defined in a typedef declaration in which the class has the typedef name for linkage purposes (7.1.3); or
a named enumeration (7.2), or an unnamed enumeration defined in a typedef declaration in which the enumeration has the typedef name for linkage purposes (7.1.3); or
an enumerator belonging to an enumeration with external linkage; or
a template, unless it is a function template that has internal linkage (clause 14); or
a namespace (7.3), unless it is declared within an unnamed namespace.
You have a named class at namespace scope, it has external linkage.
And the footnote on the bottom of page 115 of ISO/IEC 14882:2003 clarifies:
Although entities in an unnamed namespace might have external linkage, they are effectively qualified by a name unique to their translation unit and therefore can never be seen from any other translation unit.
If you have another version, try looking in section 7.3.1.1 [namespace.unnamed]
That's not a valid example of the rule. The hidden class in your example has external linkage. (It has a compiler-generated unique name such that nothing outside the current translation unit can actually link with it, but it's still external.)
The standard gives an example of a local type:
template <class T> class X { /* ... */ };
void f()
{
struct S { /* ... */ };
X<S> x3; // error: local type used as template-argument
X<S*> x4; // error: pointer to local type used as template-argument
}