Using declaration and multiple inheritance - c++

We know that a 'using declaration' for a namespace's member name in a scope where another entity is defined there with the same name, causes a compile time error: "symbol x is already defined".
The error is detected at the point the 'using declaration' appears not at use point like a 'using directive'.
A using declaration in a class scope allows only exposing a base class member name.
Here is an example:
#include <iostream>
using namespace std;
struct Foo{
Foo(int x) : x_(x){}
int x_ = 0;
};
struct Bar{
Bar(int y) : y_(y){}
int y_ = 0;
};
struct FooBar : Foo, Bar{
using Foo::Foo; // inheriting Foo's ctors
using Bar::Bar; // inheriting Bar's ctors
int x_ = 10;
// using Foo::x_; // error detected here
};
int main(){
FooBar fb(5); // error detected here
}
As you can see above the using declaration for Foo::x_ causes a compile-time error at the point the using appears and it is OK.
But the using declaration for base classes Foo and Bar constructors don't issue an error until the usage in main?!!
Normally FooBar inherits Foo(int); so when using Bar::Bar; appears normally the compiler complains but the code works fine until I try to use that constructor. FooBar(int).
After inheriting all the constructors, the compiler defines a derived class ctor and make it call its base class one passing in its parameters to it so it is as if we wrote:
struct FooBar : Foo, Bar{
FooBar(int x) : Foo(x){}
FooBar(int x) : Bar(x){}
//...
};
So why the compiler allows such bug? and wait until the use of that ctor to flag ambiguity??
It looks to me that a using declaration in such case undergoes the same thing like a using directive.

It is a general (though not universal) principle of C++ that a prefix of a program is valid if and only if there is some valid completion of it that uses the declarations in the prefix. This is why it’s not an error to declare an unused variable: it could be used, and it’s “too late” to reject it later if it’s not. Here, that principle is generalized a bit to say that, because overload resolution can usually distinguish between functions from different namespaces or base classes, there is no check for conflict at all.
Not so for variables, since you can’t possibly use the ambiguous combination. Also not so if one of the conflicting functions declarations is a direct member and a sibling of the using-declaration: in that case, you can’t possibly use the function you declared, so you are presumed not to have meant it.

Related

Function overload outside class not seen

Consider following code snippet:
enum class Bar {
A
};
void foo(Bar) {}
struct Baz {
void foo() {
foo(Bar::A);
}
};
It fails to compile, the message from gcc 9.2 is:
:12:19: error: no matching function for call to 'Baz::foo(Bar)'
12 | foo(Bar::A);
|
I don't suspect it is a bug since clang 10 also fails. I have two questions regarding this situation:
Where does standard define bahaviour for such overloads?
What are the possible reasons that compiler behaviour is specified that way?
live example
The call to foo inside Baz::foo() will only look up names inside the class. If you mean to use the foo declared outside the class Baz, you need to use the scope-resolution operator, like this:
enum class Bar {
A
};
void foo(Bar) {}
struct Baz {
void foo() {
::foo(Bar::A); // looks up global 'foo'
}
};
Note that the unscoped call to foo fails because there is a Bar::foo that is found in the closest scope. If you name the function differently, then no function is found in Bar, and the compiler will look in the outer scope for the function.
enum class Bar {
A
};
void foo(Bar) {}
struct Baz {
void goo() { // not 'foo'
foo(Bar::A); // this is fine, since there is no 'Bar::foo' to find
}
};
Here's the quote from cppreference for a class definition.
e) if this class is a member of a namespace, or is nested in a class that is a member of a namespace, or is a local class in a function that is a member of a namespace, the scope of the namespace is searched until the definition of the class, enclosing class, or function. if the lookup of for a name introduced by a friend declaration: in this case only the innermost enclosing namespace is considered, otherwise lookup continues to enclosing namespaces until the global scope as usual.
Of course, this only applies to class definitions, but for member functions (which is your example), it says
For a name used inside a member function body, a default argument of a member function, exception specification of a member function, or a default member initializer, the scopes searched are the same as in [class definition], ...
So the same logic applies.
According to the rule of unqualified name lookup, from the standard, [basic.lookup.unqual]/1,
(emphasis mine)
In all the cases listed in [basic.lookup.unqual], the scopes are searched for a declaration in the order listed in each of the respective categories; name lookup ends as soon as a declaration is found for the name.
That means the name foo is found at the class scope (i.e. the Baz::foo itself), then name lookup stops; the global one won't be found and considered for the overload resolution which happens later.
About your 2nd question, functions can't be overloaded through different scopes; which might cause unnecessary confusion and complexity. Consider the following code:
struct Baz {
void foo(int i) { }
void foo() {
foo('A');
}
};
You know 'A' would be converted to int then passed to foo(int), that's fine. If functions are allowed to be overloaded through scopes, if someday a foo(char) is added in global scope by someone or library, behavior of the code would change, that's quite confusing especially when you don't know about the adding of the global one.

Creating explicitly specialized template class object yields "object has initializer but incomplete type" error

Simple case. I don't quite understand why the parentheses are necessary for calling the default ctor of the explicitly instantiated template.
And, why calling the non-default ctor of the explicitly instantiated template gives me the "incomplete type" error?
Thank you very much!
// X.h
template <const int MODE>
class X{
public:
X() = default;
X(int& a) {++a;}
// define X here
};
// declare the explicit specialization
template <> class X<1>;
// Then define the default behaviors of X.
// X.cpp
#include "X.h"
template <>
class X<1>{
public:
X() = default;
X(int& a) {--a;}
// define X<1>
};
// Then specialize behavior.
// main.cpp
#include "X.h"
int main(){
X<2> x_2; // fine, instantiates the default version
X<1> x_1(); // Edit: A function declaration, as pointed out in the comment.
X<1> x_1_1; // error: aggregate ‘X<1> x_1_1’ has incomplete type and cannot be defined
int a = 0;
X<1> x_1_2(a); // error: variable ‘X<1> x_1_2’ has initializer but incomplete type
}
As pointed out by others X<1> x_1(); is only a function declaration, so it doesn't actually instantiate an object of type X<1>. For the incomplete type error: You have to put the whole declaration of X<1> into the header file (not only a forward declaration, as you did now). You can put the implementation in the cpp file, but anyone using objects (and not only pointers to objects) of type X<1> (in this case: main) has to know how large it is and what methods it provides.
Your confusion might in part stem from the way you use specialisation in your example: In your specialisation, the only thing that differs from the general template is the definition of one of the constructors (all the signatures stay the same). So you might have thought the compiler could figure this out by itself. In fact, it cannot possibly do that, because your specialised class might look completely different (with different constructors, different members/member functions) from the unspecialised template. Like this:
template <int I>
struct X {
bool hello(int x, int y);
};
template<>
struct X<1> {
int goodbye(std::string x);
};
If the compiler only sees template<> struct X<1>; instead, how should it figure out the interface of this class?
template <> class X<1>; is just a forward declaration of the specialization, and conveys no information about the layout of the type. Since the actual specialization is not visible from main.cpp (it is defined in X.cpp), the type is indeed incomplete.
Keep in mind that class template specializations share nothing with the template class other than its base name, so the compiler has no idea how many bytes to allocate on the stack for each instance (nor whether the requested constructor even exists!) unless it knows the definition of the specialization, which you have hidden away in a .cpp file.
This is akin to doing class Foo; and then trying to declare a variable of type Foo without providing a definition of the type.

members of a class known at any point inside class

I always thought that if I declare member of a class inside class this member is known at the entire scope of a class that is:
class X
{
public:
X(int a) :v_(a)
{}
private:
int v_;//even though v_ is declared here I'm using it in ctor which is above this line
};
So that makes sense to me.
Anyhow this doesn't because I'm getting error that v_ isn't known
class X
{
public:
X(decltype(v_) a) :v_(a)//error on this line, compiler doesn't know v_
{}
private:
int v_;
};
Would be glad to learn why.
I'm using intel compiler v14 SP1
Thank you.
3.3.7 Class scope
1 The following rules describe the scope of names declared in classes.
1) The potential scope of a name declared in a class consists not only of the declarative region following
the name’s point of declaration, but also of all function bodies, brace-or-equal-initializers of non-static
data members, and default arguments in that class (including such things in nested classes).
...
That means that you can use v_ in function bodies, constructor initializer lists and default arguments. You are not allowed to use v_ in parameter declarations the way you used it in your code.
For example, this shall compile
class X
{
public:
X(int a = decltype(v_)()) : v_(a)
{}
private:
int v_;
};
but not the second example in your original post.
Your code compiles with Clang.
Reading C++11 specifications you are not allowed to declare the variable after it is being used as function/constructor parameter.
In many cases classes including function signatures will be defined in headers, but function bodies in cpp files. Since the header will have been read by the compiler at the start of reading the cpp file, this problem does usually not occur. But indeed, C++ compilers don't look ahead.

Why class forward declaration is not allowed in function scope?

Below code works fine:
template<typename T> class X {};
class A; // line-1
void foo(); // line-2
int main ()
{
X<A> vA;
}
class A {};
void foo() {}
Let line-1 and line-2 are moved inside main(). The function doesn't get affected but the class A forward declaration doesn't work and gives compiler error:
error: template argument for template<class T> class X uses local
type main()::A
What you can observe is happening because, in C++, you can define classes inside functions.
So, if you place class A; in main, you are forward-declaring a class in scope of this function (i.e. class main::A), not in global scope (class A).
Thus, you are finally declaring an object of type X with a template argument of undefined class (X<main::A>).
error: template argument for template class X uses local type main()::A
This is the real problem - using a local type. In C++03 you cannot use local types as template arguments, because nobody had figured out how to name the resulting types.
What if you have several class A in several overloaded functions (again using the same name) - would the resulting X<A>'s then be the same type, or different types? How would you tell them apart?
In C++03 the standard passed on this, and just said "Don't do that!".
The problem was resolved in C++11 by deciding that X<A> using a local type A would be the same as if A had been declared in an anonymous namespace outside of the function, like
namespace
{
class A
{ };
}
int main()
{
X<A> vA;
}
So, with a newer compiler (or using a -std=cpp11 option), you can use a local class, and we also know what it means.
However, forward declaring a type inside a function still doesn't mean the same as forward declaring it in another scope. They will just be different types.

Can I use using inside a fnc def?

I've a construct of this form:
template<class X>
struct Base
{
X get_data()
{
return X();
}
};
template<class X>
struct Derived : Base<X>
{
X do()
{
auto v = get_data();//this get is from Base
}
};
I tried to use this get as it is shown but I was getting error : there are no arguments to 'get_data' that depend on a template parameter, so a declaration of 'get_data' must be available [-fpermissive]|
Ok so I've tried:
X do()
{
using Base<T>::get_data;
auto v = get_data();//this get is from Base
}
And I've got the following error: 'Derived<T>::Base' is not a namespace. Now, here I've got a problem, because as a matter of fact formally Base being a struct is a namespace (special form of) but anyway, I declared using Base<T>::get_data; outside of any fnc and this compiles. So the Q is: Is that a compiler error or using 'using declaration' is permitted inside a fnc body?
using directives for base class member names only make sense on the class level, where they serve to make names of base class members visible that might otherwise be hidden.
They do not make sense at function scope, and thus aren't allowed there.
What you can do is this:
struct Derived : public Base<X>
{
using Base<X>::get_data;
//...
};
(This is only sensible here because of your template context and the fact that get_data() doesn't depend on any template parameters. In an ordinary class, you wouldn't need this at all if you aren't deliberately hiding the function.)
At function scope, the only using directives that are allowed are those that bring in names from other namespaces for the purpose of argument-dependent lookup.
Also note that do is a C++ keyword.
Replace
using Base<T>::get_data;
with
using Base<X>::get_data;
auto v = this->get_data();