C++ operator-comma / forward reference code doesn't compile - c++

The following code doesn't compile, unless the commented line is uncommented:
template <class T> struct R { static T& r(); };
struct M {
static char m(M&);
template <class T> static int m(const T&);
};
template <class T> struct A;
template <class T>
struct B {
struct U { };
struct V { M& operator,(U); };
enum { v = sizeof(M::m((R<V>::r(), R<A<T>*>::r()))) };
};
template <class T> struct A { B<T> y; };
int main()
{
// A<int>(); // Works if uncommented.
B<int>();
}
At the comma operator, the compiler thinks that it needs A<int> to be complete, even though the code only traffics in A<T>*. I don't understand why. It fails with both clang and g++. Clang says
h.cpp:13:36: error: field has incomplete type 'B<int>'
template <class T> struct A { B<T> y; };
^
h.cpp:11:38: note: in instantiation of template class 'A<int>' requested here
enum { v = sizeof(M::m((R<V>::r(), R<A<T>*>::r()))) };
^
h.cpp:17:5: note: in instantiation of template class 'B<int>' requested here
B<int>();
^
h.cpp:8:8: note: definition of 'B<int>' is not complete until the closing '}'
struct B {
^
1 error generated.
I'm now sitting in the debugger debugging the compiler :-) I think what's happening is that the compiler is using argument-dependent lookup to find matching operator,s and the associated classes and namespaces for a pointer to a class include the class itself, so the compiler wants the class to be complete. Maybe.

you get the same error message if you change main to be this:
int main()
{
// A<int>(); // Works if uncommented.
auto& x = R<A<int>*>::r();
B<int>();
}
I'm going to go out on a limb here and say that it's as you hinted. Mentioning a pointer to an A<int> does not cause a template expansion of A<int>.
This makes sense (to me), since it's the same as mentioning a pointer to a forward-declared type - you don't need the type fully defined at that point.
Perhaps someone wiser than me can find the passage in the standard that mandates this.

Related

c++ partial template specialization with requires statement: error: out-of-line definition of 'foo' from class Bar<T> without definition

Consider the following code which attempts to implement a partial specialization of class Bar. In the first case, the foo member function is defined inline and in the second case out of line. The out of line definition produces a compile error which I cannot figure out:
error: out-of-line definition of 'foo' from class 'Bar<T>' without definition
template<class T>
struct Bar;
template<class T>
requires std::is_same_v<T, int>
struct Bar<T> {
int foo(T a) {
return a + 5;
}
};
template<class T>
requires std::is_same_v<T, double>
struct Bar<T> {
double foo(T a);
};
template<class T>
requires std::is_same_v<T, double>
double Bar<T>::foo(T a) {
return a + 5;
};
I am using clang-11 with the c++20 compilation option. I am unsure if this is my misunderstanding, a feature or a bug. Any help is appreciated.
Might be clang bug. It was reported at https://bugs.llvm.org/show_bug.cgi?id=50276.
Anyway GCC is fine

Is it abuse to deduce parameters of parent template when passing pointer to constexpr function in the scope of a class

Minimal example I got is a bit complicated:
struct A { };
template <int>
struct Parent { };
template <int N>
constexpr int operator*(A, Parent<N>*) { return N; }
template <class T>
using ptr = T*;
template <int>
struct Other { };
template <int N>
struct Kid: Parent<N> {
static Other<A{} * ptr<Kid>{}> o;
};
int main() {
Kid<2>{};
}
[gcc] compiles the code without any problem, [clang] complains about matching Parent against Kid problem:
prog.cc:7:15: note: candidate template ignored: could not match 'Parent' against 'Kid'
constexpr int operator*(A, Parent<N>*) { return N; }
To get more absurd when we change the code a bit:
struct A { };
template <int>
struct Parent { };
template <int N>
constexpr int operator*(A, Parent<N>*) { return N; }
template <class T>
using ptr = T*;
template <int>
struct Other { };
template <int N>
struct Kid: Parent<N> {
static constexpr int s = A{} * ptr<Kid>{};
};
int main() {
Other<Kid<2>::s>{};
}
[clang] also compiles the code. So... is it a bug or am I starting to go insane?
Clang is right to the letter of the law. Yes, Kid is derived from Parent. But that relationship can only be established once Kid is a completely defined type. And well, [class.mem]/6:
A class is considered a completely-defined object type ([basic.types])
(or complete type) at the closing } of the class-specifier. Within the
class member-specification, the class is regarded as complete within
function bodies, default arguments, noexcept-specifiers, and default
member initializers (including such things in nested classes).
Otherwise it is regarded as incomplete within its own class
member-specification.
We are in the "otherwise" part I highlighted. Even though we are dealing with pointers, the class is not yet considered defined for the pointer conversion to be valid. GCC is overly permissive.

Full class template specialization with forward declarations

It appears a forward declaration is causing an issue when specializing some template functions within a template class. I am specializing the class also as it's necessary in order to specialize the function, and this seems to be causing the issue.
Edit: Second question about pre-creating functions for process function:
processor.H
namespace OM{
template<typename MatchT> //fwd decl. ERROR 2. see below.
class Manager;
template<typename MatchT>
class Processor
{
public:
Processor(Manager<MatchT>& mgr_):_manager(mgr_) {}
template<int P>
void process();
void doProcess();
private:
Manager<MatchT>& _manager;
template<int P, int... Ps>
struct table : table<P-1,P-1, Ps... > {};
template<int... Ps>
struct table<0, Ps...>
{
static constexpr void(*tns[])() = {process<Ps>...};
};
static table<5> _table;
};
}
#include "processor.C"
processor.C
namespace OM{
#include "MyManager.H" (includes MyManager/MyConfig)
template<typename MatchT>
template<int P>
inline void Processor<MatchT>::process()
{
...
_manager.send(); //this works..
}
template <> template <>
inline void Processor<MyManager<MyConfig> >::process<1>()
{
_manager.send(); //ERROR 1 - see below.
}
//ERROR here:
template<typename MatchT>
void doProcess()
{
Processor<MatchT>::_table::tns[2](); ERROR 3 below.
}
}
compile errors:
1. error: invalid use of incomplete type 'class Manager <MyManager<MyConfig> >'
2. error: declaration of 'class Manager<MyManager<MyConfig> >'
class Manager;
3. error: no type name '_table' in "class Processor<MyManager<MyConfig> >'
I'm not calling this from a specialized function, so I'm not sure
why I'm getting this.
I can move things around a bit to ensure the _manager calls are not within the specialized functions, but I'd rather not if I don't have to.
I played around with this, I think now I get a similar result.
The problem is the template specialisation and forward declaration together. This should be eqvivalent:
template<typename T> struct A;
template<typename T> class B
{
template<int N>
T f();
};
template<typename T> class B<A<T>>
{
A<T> *a;
template<int N>
T f();
};
template<typename T> struct A{ T i=1; };//works
template<>
template<>
int B<A<int>>::f<1>()
{
return a->i + 1;
}
//template<typename T> struct A { T i = 1; };//error
int main()
{
B<A<int>> b;
}
The compilation for templates comes in two stages:
First, it checks syntax and (some) dependence. So, for example if a in B<A<T>> was not a pointer/reference, but the object itself, it could compile, if that B<A<T>> is constructed after A is defined. (worked for me)
So the second is when the compiler inserts the arguments, here, the compiler must know all objects to generate code.
When fully specialising, as above, the compiler is forced to know all types. It already knows, that f function depends on the implementation of A, so it cannot generate the code.
Therefore you have to define A or Manager before the function specialisation.

Is accessing member variables in case of multiple Inheritance compiler dependent? How to do it correctly?

I have for following Situation:
template <class K> class A
{
public:
int a;
};
class B
{
public:
virtual void DoSomething() = 0;
};
template <class K> class C : public A<K>, public B
{
public:
virtual void DoSomething()
{
a = 3; // ***
}
};
Now this works on MSVC, however gcc tells me for the line with the 3 stars : "error: 'a' was not declared in this scope". I figured out that I can replace the line with
A::a = 3;
and it works on gcc (well mingw) as well. Do we have to add the original class name all the time to be standard conform? I thought, I only have to add it if names collide otherwise.
I'm using mingw32 (gcc) 4.8.1.
Now it makes sense...
GCC is right, and MSVC is wrong. It should not work because a is template dependent but the expression a; is not.
That is, the meaning you intend of a depends on a template argument (in your case K), but the expression that contains it does not. And since the compiler cannot be sure that the instantiation of A<K> will have a member named a, it simple assumes it does not.
There are several easy solutions, all of them involve using a template dependent expression so resolve a:
this->a = 3; //my favourite
A<K>::a = 3; //you found this one
EXAMPLE:
Let's see an example of why it should not work:
template <typename K> struct A
{
int a;
};
int a; //global variable to make things more interesting
template <typename K> struct B : A<K>
{
void foo()
{
a = 3; //does it refer to A<K>::a or ::a?
}
};
//template specialization of A<int>
template <> struct A<int>
{
};
B<float> bf; // what does bf.foo() do?
B<int> bi; //and bi.foo()?
Also, you can make the opposite situation:
template <typename K> struct A
{
};
int a; //global variable to make things more interesting
template <typename K> struct B : A<K>
{
void foo()
{
a = 3; //does it refer to A<K>::a or ::a?
}
};
//template specialization of A<int>
template <> struct A<int>
{
int a; //now the member variable is in the specialization
};
B<float> bf; // what does bf.foo() do?
B<int> bi; //and bi.foo()?
As you can see, the standard C++ behavior is there for you protection, so that the code you write in a template will always refer -more or less- to the same things:
a: will be always the non template (global in the example) variable, if available. If not, it is a compiler error.
this->a: will be always the member variable, if available. If not, a compiler error.

GCC issue: using a member of a base class that depends on a template argument

The following code doesn't compile with gcc, but does with Visual Studio:
template <typename T> class A {
public:
T foo;
};
template <typename T> class B: public A <T> {
public:
void bar() { cout << foo << endl; }
};
I get the error:
test.cpp: In member function ‘void B::bar()’:
test.cpp:11: error: ‘foo’ was not declared in this scope
But it should be! If I change bar to
void bar() { cout << this->foo << endl; }
then it does compile, but I don't think I have to do this. Is there something in the official specs of C++ that GCC is following here, or is it just a quirk?
David Joyner had the history, here is the reason.
The problem when compiling B<T> is that its base class A<T> is unknown from the compiler, being a template class, so no way for the compiler to know any members from the base class.
Earlier versions did some inference by actually parsing the base template class, but ISO C++ stated that this inference can lead to conflicts where there should not be.
The solution to reference a base class member in a template is to use this (like you did) or specifically name the base class:
template <typename T> class A {
public:
T foo;
};
template <typename T> class B: public A <T> {
public:
void bar() { cout << A<T>::foo << endl; }
};
More information in gcc manual.
Wow. C++ never ceases to surprise me with its weirdness.
In a template definition, unqualified names will no longer find members of a dependent base (as specified by [temp.dep]/3 in the C++ standard). For example,
template <typename T> struct B {
int m;
int n;
int f ();
int g ();
};
int n;
int g ();
template <typename T> struct C : B<T> {
void h ()
{
m = 0; // error
f (); // error
n = 0; // ::n is modified
g (); // ::g is called
}
};
You must make the names dependent, e.g. by prefixing them with this->. Here is the corrected definition of C::h,
template <typename T> void C<T>::h ()
{
this->m = 0;
this->f ();
this->n = 0
this->g ();
}
As an alternative solution (unfortunately not backwards compatible with GCC 3.3), you may use using declarations instead of this->:
template <typename T> struct C : B<T> {
using B<T>::m;
using B<T>::f;
using B<T>::n;
using B<T>::g;
void h ()
{
m = 0;
f ();
n = 0;
g ();
}
};
That's just all kinds of crazy. Thanks, David.
Here's the "temp.dep/3" section of the standard [ISO/IEC 14882:2003] that they are referring to:
In the definition of a class template or a member of a class template, if a base class of the class template depends on a template-parameter, the base class scope is not examined during unqualified name lookup either at the point of definition of the class template or member or during an instantiation of the class template or member. [Example:
typedef double A;
template<class T> class B {
typedef int A;
};
template<class T> struct X : B<T> {
A a; // a has typedouble
};
The type name A in the definition of X<T> binds to the typedef name defined in the global namespace scope, not to the typedef name defined in the base class B<T>. ] [Example:
struct A {
struct B { /* ... */ };
int a;
int Y;
};
int a;
template<class T> struct Y : T {
struct B { /* ... */ };
B b; //The B defined in Y
void f(int i) { a = i; } // ::a
Y* p; // Y<T>
};
Y<A> ya;
The members A::B, A::a, and A::Y of the template argument A do not affect the binding of names in Y<A>. ]
This changed in gcc-3.4. The C++ parser got much more strict in that release -- per the spec but still kinda annoying for people with legacy or multi-platform code bases.
The main reason C++ cannot assume anything here is that the base template can be specialized for a type later. Continuing the original example:
template<>
class A<int> {};
B<int> x;
x.bar();//this will fail because there is no member foo in A<int>
VC doesn't implemented two-phase lookup, while GCC does. So GCC parses templates before they are instantiated and thus finds more errors than VC.
In your example, foo is a dependent name, since it depends on 'T'. Unless you tell the compiler where it comes from, it cannot check the validity of the template at all, before you instantiate it.
That's why you have to tell the compiler where it comes from.