g++ template bug or VC too liberal? - c++

I am writing a heavily templated piece of code in C++. It worked perfectly in VS 2005 but when I try to compile it in g++ I get some really strange errors.
The essential piece of code (simplified to a minimum, doesn't compile either) is as follows:
template <class Actual>
class Generic
{
public:
typedef Actual ThisType;
};
template <class Actual>
class Intermediate : public Generic<Actual>
{
};
template <class Q>
class Derived : public Intermediate<Derived<Q> >
{
public:
void FooBar()
{
ThisType q;
}
};
The error is:
"'ThisType' was not declared in this scope"
in the line where 'q' is being declared.
Curiously, everything works fine when Derived is not a template but a plain class. Why would compiler look into template function implementation before it is even instantiated? I know that VC++ checks far too little when compiling templates (unused templates can even contain syntactically incorrect code) - but isn't g++ checking too much here? I tried adding a typename keyword with little hope and it fails too. Is there any way to get ThisType to work as expected? I dread the thought of adding it manually to every single derived class - it's cumbersome, redundant, inelegant and error-inducing.
Best regards,
MZ

Unqualified names are not looked up in dependent base classes (your base class depends on template parameter Q). Qualify that name, and it will work.
typename Derived::ThisType q;

Trust in Comeau online compiler!
Comeau C/C++ 4.3.10.1 (Oct 6 2008 11:28:09) for ONLINE_EVALUATION_BETA2
Copyright 1988-2008 Comeau Computing. All rights reserved.
MODE:strict errors C++ C++0x_extensions
"ComeauTest.c", line 19: error: identifier "ThisType" is undefined
ThisType q;
^
"ComeauTest.c", line 19: error: expected a ";" (perhaps on the previous statement)
ThisType q;
^
2 errors detected in the compilation of "ComeauTest.c".
Dependent type names from inherited classes are not taken into account, you might try to explicitly request ThisType:
typename Intermediate<Derived<Q> >::ThisType q;

The code is indeed ill-formed. When base class depends on template parameter it is not considered by name lookup when looking for unqualified names.
In your case unqualified name ThisType will not be looked up in base class Intermediate<Derived<Q> > because your base class depends on template parameter Q.

You are missing a typename
template <class Actual> class Generic { public:
typedef Actual ThisType; };
template <class Actual> class Intermediate : public Generic<Actual> { };
template <class Q> class Derived : public Intermediate<Derived<Q> > { public:
void FooBar()
{
typename Derived::ThisType q;
return *this;
} };
int main(){}

Related

MSVC 2017 - Bug - How to declare template member function of template class X as friend of nested class X::Y

I am trying to get a cmake C++ project that compiles and runs with GCC to compile with MSVC. I am using VS 2017.
I'm not the author of the code I'm just tasked with making it compile with MSVC.
The project is large so I am unsure how can present a MCVE. However, I will try to explain as best as I can here:
I am getting this error:
Which correspinds to the friend declaration in the below sibling_iterator class.
However, leftmost_output() is a member of node.
The definition of node is appended below (see about 3/4 of the way down):
I'm pretty sure this is related to the bug in VS related to nested template classes as sibling_iterator and node are nested in a another template class.
My question is how could I make the whole node class a friend instead of specifying a specific member function? This might work failing at any other suggestions are most welcome.
Smallest reproducible code is:
#include <map>
template <class T, class T2>
struct relative_iterator : T {};
struct edge : public std::map<int, int>::iterator {};
using T_edge = edge;
class node {
public:
template <class T_iterable, class T_content>
class sibling_iterator : public relative_iterator<T_iterable, T_content>
{
public:
friend sibling_iterator<edge, T_edge> node::leftmost_output();
//..
};
static sibling_iterator<edge, T_edge> leftmost_output(); // <--move up
} ;
There are two ways to combat this:
Option 1
Move the definition of leftmost_output() above class sibling_iterator
Option 2
Make node a dependent name. If there is an alias to name which is dependent on T_iterable, then its lookup will be delayed to the time when class sibling_iterator<T_iterable, T_contents> is instantiated. The simplest way is to use standard utilities in the standard:
class sibling_iterator : public relative_iterator<T_iterable, T_content>
{
public:
static constexpr bool dependent_true = std::is_same<T_iterable,T_iterable>::value;
using dependent_node = typename std::enable_if<dependent_true, node>::type;
friend sibling_iterator<edge, T_edge> dependent_node::leftmost_output();
};
Option 2.5
But, if you prefer to define your own solution, you can define a dependet_type<T, Dependent> helper:
template <class T, class Dependent>
struct dependent_type
{
using type = T;
};
template <class T, class Dependent>
using dependent_type_t = typename dependent_type<T, Dependent>::type;
And use it:
template <class T_iterable, class T_content>
class sibling_iterator : public relative_iterator<T_iterable, T_content>
{
public:
using dependent_node = typename dependent_type<node, T_iterable>::type;
friend sibling_iterator<edge, T_edge> dependent_node::leftmost_output();
//..
};
I think that this is the best alternative, since it requires less changes to the existing code-base.
Option 2.5.5
I would have written a shorter variant:
friend sibling_iterator<edge, T_edge> dependent_type_t<node, T_iterable>::leftmost_output()
This looks perfect, since it requires minimal changes to the source code. I would have written it, had it not made the compiler crash:
fatal error C1001: An internal error has occurred in the compiler.
(compiler file 'msc1.cpp', line 1469)
I am getting this error:
error C2039: 'leftmost_output': is not a member of 'mv::graph::node'
[...]
However, leftmost_output() is a member of node.
The definition of node is appended below (see about 3/4 of the way
down):
Apparently, the VC compiler doesn't find the definition of node, as it refers to something in a different scope. Try moving the friend declarations below the definition of the nested node class.

Inherit base constructor with base class being a template parameter (MSVC)

I try to achieve this code using VS2017, and get errors :
template <class T>
class A {
public :
A() {}
};
template < template <class U> class T, class U>
class B : public T<U> {
using T<U>::T;//Errors : # 'T': is not a member of 'A<U>' # 'T': symbol cannot be used in a using-declaration
};
int main() {
B<A, int> test;
return 0;
}
Works perfectly using Clang and GCC according to https://wandbox.org/
I'd like to know why it's not working on Visual Studio, and how to fix it. Looks like VS doesn't want to consider second 'T' parameter as a template.
Here is another question previously asked the closest I could find about this matter. Couldn't find a solution reading it though :
Inheriting constructors from a template base class
The workaround is to define a typedef alias
typedef T<U> base_t;
and use it to inherit the constructors from the base class:
using base_t::base_t;
This is not a unique compiler bug; I dimly recall a similar issue with an old version of gcc.

Is this a bug in GCC?

EDIT: This is not a bug, just me not knowing about dependent name lookups in templated base classes (which MSVC "helpfully" resolves without errors).
I wrote a functor implementation a while back, and a simple "Event" wrapper that uses it. It compiles fine under MSVC, but GCC gives an error about a member variable in the base class, subscribers, not being declared; changing subscribers to this->subscribers resolves the issue(!). It appears to happen only with the curiously recurring template pattern, and with partial template specialization.
Simplified source (sorry for the mind-bending template usage...):
#include <vector>
template<typename TEvent>
struct EventBase
{
protected:
std::vector<int> subscribers;
};
template<typename TArg1 = void, typename TArg2 = void>
struct Event : public EventBase<Event<TArg1, TArg2> >
{
void trigger(TArg1 arg1, TArg2 arg2) const
{
// Error on next line
auto it = subscribers.cbegin();
}
};
template<typename TArg1>
struct Event<TArg1, void> : public EventBase<Event<TArg1> >
{
void trigger(TArg1 arg1) const
{
// Using `this` fixes error(?!)
auto it = this->subscribers.cbegin();
}
};
template<>
struct Event<void, void> : public EventBase<Event<> >
{
void trigger() const
{
// No error here even without `this`, for some reason!
auto it = subscribers.cbegin();
}
};
int main()
{
return 0;
}
Am I invoking undefined behaviour somewhere? Is my syntax somehow wrong? Is this really a bug in GCC? Is it perhaps a known bug? Any insight would be appreciated!
More details: Compiled using g++ -std=c++11 main.cpp. I'm using GCC version 4.7.2. Exact error message:
main.cpp: In member function ‘void Event<TArg1, TArg2>::trigger(TArg1, TArg2) const’:
main.cpp:17:15: error: ‘subscribers’ was not declared in this scope
This is a bug in MSVC instead. Names from dependent base classes have to be "thisambiguated".
The reason is that unqualified lookup of dependent names proceeds in two phases. During the first phase, the base class is not yet known and the compiler cannot resolve the name. MSVC does not implement two-phase name lookup and delays the lookup until the second phase.
The full specialization
template<>
struct Event<void, void> : public EventBase<Event<> >
{
void trigger() const
{
// No error here even without `this`, for some reason!
auto it = subscribers.cbegin();
}
};
does not suffer from this problem, because both the class and its base are regular classes, not class templates, and there is no template dependency to begin with.
When porting C++ code from MSVC to gcc/Clang, dependent name lookup disambiguation and the template keyword disambiguation (i.e. calling member function template using ::template, ->template or .template syntax) are two of the subtleties that you have to deal with (empty base optimization is another one). For all the Standards compliance rhetoric, this will probably never be fixed for reasons of backwards compatibility.

Using friend in templates

I am writing a small class, the class is basically a factory for the C class, but I want other classes to be able to access some of the methods.
template<class C>
class CFactory {
public:
friend class C;
};
This should make the fields of CFactory available to the class C, but the compiler thinks otherwise.
I'm getting the following two errors using gcc on a mac.
error: using template type parameter 'C' after 'class'
error: friend declaration does not name a class or function
Can anyone tell me what I'm doing wrong and how to get et right?
Unfortunately, in my understanding, this isn't allowed in current standard.
§7.1.5.3/2 says:
[Note: ... within a class template
with a template type-parameter T, the
declaration
friend class T;
is ill-formed. -end note]
On a related note, this restriction seems to be removed in C++0x
(§11.3/3 in N3290).
Incidentally, MSVC may allow this if we write simply friend T;.
Ise's response is correct -- Comeau's FAQ contains a question concerning this issue in more detail.
However, perhaps you can try an extra template indirection that might work? Something like this:
template <typename T>
struct FriendMaker
{
typedef T Type;
};
template <typename T>
class CFactory
{
public:
friend class FriendMaker<T>::Type;
};
This seems to only work with gcc 4.5.x however so I wouldn't rely on it.
C can be anything - int, double, etc. and they are not classes. In general, this class C is not class. And you cannot have something like:
class A
{
friend class int;
//..
};
One more reason to prefer typename, instead of class when using templates

C++ templated function and forward declarations

I'm working on some code that compiles and links (and even has released commercial products) on Windows using MSVC. It doesn't compile with GCC though, I get the following errors:
.../CBaseValue.h: In member function 'bool CBaseValue::InstanceOf()':
.../CBaseValue.h:90:18: error: invalid use of incomplete type 'struct CValueType'
.../CBaseValue.h:11:7: error: forward declaration of 'struct CValueType'
CBaseValue.h
class CValueType;
class CBaseValue {
public:
...
template <typename _Type>
bool InstanceOf() {
CValueType* pType = GetType();
if(pType == NULL) {
return false;
}
else {
return pType->IsDerivedFrom<_Type>();
}
}
...
}
CValueType.h
class CValueType : public CBaseValue {
public:
...
template <typename _Type>
bool IsDerivedFrom() {
return IsDerivedFrom(_Type::TYPEDATA);
}
...
}
I understand why this is a problem. The base class (CBaseValue) has a templated function that uses a derived class (in this case CValueType).
It looks like MSVC isn't exactly obeying the C++ spec here and I've just been bitten by it. But the MSVC behavior of using the forward declaration until code calling the templated function is actually compiled is also more desirable right now. Does anybody know of a work-around where I can get this code working with GCC without having to rewrite a lot of base code?
From my own research it looks like passing '-fno-implicit-templates' to g++ would help but then I'd need to explicitly define the called template types. There are a lot of them so if I can avoid that I'd prefer it. If the general consensus is that this is my best option... so be it!
And in case anybody is wondering, I'm porting the code over to the Mac which is why we're now using GCC.
This is ill-formed by the Standard, but no diagnostic is required. MSVC is fine not diagnosing this particular case (even when instantiation happens!).
More specifically, the (C++03) Standard rules at 14.6/7
If a type used in a non-dependent name is incomplete at the point at which a template is defined but is complete at the point at which an instantiation is done, and if the completeness of that type affects whether or not the program is well-formed or affects the semantics of the program, the program is ill-formed; no diagnostic is required.
So the solution is to just make the type dependent, but arrange it that during instantiation, that type is designated. For example, you can do that by rewriting your template like this
template<typename T, typename> // just ignore second param!
struct make_dependent { typedef T type; };
template <typename Type> // eww, don't use "_Type" in user code
bool InstanceOf() {
typename make_dependent<CValueType, Type>::type* pType = GetType();
// ...
return pType->template IsDerivedFrom<Type>();
// ...
}
It seems that the CBaseValue::InstanceOf() function is useless to anyone not including CValueType.h.
So wait to provide the definition until all needed types are available. (EDIT: This is exactly what is suggested by Charles Bailey's comment that he posted while I was typing -- I guess we think alike.)
CBaseValue.h
class CValueType;
class CBaseValue {
public:
...
template <typename _Type>
bool InstanceOf();
...
}
CValueType.h
class CValueType : public CBaseValue {
public:
...
template <typename T>
bool IsDerivedFrom() {
return IsDerivedFrom(T::TYPEDATA);
}
...
}
template <typename T>
inline bool CBaseValue::InstanceOf() {
CValueType* pType = GetType();
if(pType == NULL) {
return false;
}
else {
return pType->IsDerivedFrom<T>();
}
}
They seem very tightly coupled though, so maybe having just one header file for both classes, or one public header file that includes the individual headers in the correct order, would be better.