Allow forward class declarations to match a type alias.

165 views
Skip to first unread message

Bengt Gustafsson

unread,
Jun 29, 2018, 3:56:26 AM6/29/18
to ISO C++ Standard - Future Proposals
A forward declaration of a class name is currently not allowed to be resolved by a type alias of a class template. I don't see the logic to this, why is this illegal:

// forward declaration in one header file.
class FloatImage;


// Definition in another header file.
template<typename T> class CudaImage {
};

using FloatImage = CudaImage<float>;


Godbolt: https://godbolt.org/g/mmTTeQ shows that all compilers reject this.

This has  created a fair amount of trouble for me when trying to refactor what was previously a FloatImage class to a generic pixel type template. I don't see the logic to making this
illegal as FloatImage is still the name of a class, i.e. an instance of the CudaImage class template.

While I can update the forward declaration to:
// forward declaration in one header file.
template<typename T> class CudaImage;
using FloatImage = CudaImage<float>;

This seems to be an unnecessary leakage of implementation detail.

so is there a technical reason preventing this from being allowed or is it just an oversight that could be corrected?

mihailn...@gmail.com

unread,
Jun 29, 2018, 4:48:26 AM6/29/18
to ISO C++ Standard - Future Proposals
We have to see what is the situation after Modules. 

For now your best bet is to actually create a class 
class FloatImage : public CudaImage<float> { /*using …*/ };

Justin Bassett

unread,
Jun 29, 2018, 11:29:03 AM6/29/18
to std-pr...@isocpp.org
I would extend it and say you can forward declare an arbitrary type, like in this thread: https://groups.google.com/a/isocpp.org/forum/#!msg/std-proposals/fm-ThgXXjF4/fukkKkh8Xh0J;context-place=forum/std-proposals

As much as I want universal forward declarations, I remember seeing that it can't be done, since some pointers can have different representations / sizes. It mightve been something like void* or char* could be bigger and are on certain systems. I couldn't find a link to back this up, though

--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/975ab4cd-2ff0-4a09-9fac-7c37dc67cf16%40isocpp.org.

Gašper Ažman

unread,
Jun 29, 2018, 11:31:28 AM6/29/18
to std-pr...@isocpp.org
AFAIK it's not possible because a using declaration is not a linkable symbol - you need to know the actual type name in order to mangle things.

Then again perhaps you can skirt this by just not using it anywhere you'd actually need a symbol.

G

To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.

--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.

To post to this group, send email to std-pr...@isocpp.org.

Nicol Bolas

unread,
Jun 29, 2018, 11:41:10 AM6/29/18
to ISO C++ Standard - Future Proposals


On Friday, June 29, 2018 at 3:56:26 AM UTC-4, Bengt Gustafsson wrote:
A forward declaration of a class name is currently not allowed to be resolved by a type alias of a class template. I don't see the logic to this, why is this illegal:

// forward declaration in one header file.
class FloatImage;


// Definition in another header file.
template<typename T> class CudaImage {
};

using FloatImage = CudaImage<float>;


OK, so you want to say that `FloatImage` is a class, then you want to say it's a type alias. Does that not seem... incoherent to you?

Is `is_class_v<FloatImage>` supposed to be true or false? After all, you could have done `using FloatImage = int;` later on, which would change the result. Or you could do `using FloatImage = SomeUnionType;`, which again changes the result.

Remember: incomplete types can be used to instantiate a lot of the type traits. `class FloatImage` is not the same thing as "hey compiler, there's a typename called `FloatImage`, but you can't use it for anything right now." If you want to be able to say that, then you need a new kind of type name. Right now, types can be complete (meaning the full definition is available) or incomplete (meaning that the full definition is not available, but we do know something about it). You want a third category, where we no nothing more than that a name is a type.

Richard Hodges

unread,
Jun 29, 2018, 12:43:07 PM6/29/18
to std-pr...@isocpp.org
I am wondering if your motivation is an XY problem, please see inline.

On Fri, 29 Jun 2018 at 09:56, Bengt Gustafsson <bengt.gu...@beamways.com> wrote:
A forward declaration of a class name is currently not allowed to be resolved by a type alias of a class template. I don't see the logic to this, why is this illegal:

// forward declaration in one header file.
class FloatImage;


// Definition in another header file.
template<typename T> class CudaImage {
};

using FloatImage = CudaImage<float>;


Godbolt: https://godbolt.org/g/mmTTeQ shows that all compilers reject this.

This has  created a fair amount of trouble for me when trying to refactor what was previously a FloatImage class to a generic pixel type template. I don't see the logic to making this
illegal as FloatImage is still the name of a class, i.e. an instance of the CudaImage class template.

It seems to me that you previously forward-declared the class FloatImage, which was later defined.

The might have looked like this:

class FloatImage;
// we can reference the type before definition
extern void foo(FloatImage& fi);


Later, before using a FloatImage, you would define the class.

The new code would need to look like this:

template<typename T> class CudaImage;
using FloatImage = CudaImage<float>;
// we can *still* reference the type before definition
extern void foo(FloatImage& fi);


Later, but before actually using a FloatImage, you would define the template:

template<class T> class CudaImage
{
};

 

While I can update the forward declaration to:
// forward declaration in one header file.
template<typename T> class CudaImage;
using FloatImage = CudaImage<float>;

This seems to be an unnecessary leakage of implementation detail.

I think the above solution resolves this leakage.
 

so is there a technical reason preventing this from being allowed or is it just an oversight that could be corrected?

I don't think in reality that it's needed, or that the lack of the feature is an oversight.
 

--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.

To post to this group, send email to std-pr...@isocpp.org.

Nicol Bolas

unread,
Jun 29, 2018, 2:03:09 PM6/29/18
to ISO C++ Standard - Future Proposals
On Friday, June 29, 2018 at 12:43:07 PM UTC-4, Richard Hodges wrote:
I am wondering if your motivation is an XY problem, please see inline.

It should also be noted that reflection will be able to see the difference between the use of a genuine typename and a type alias.

Brian Bi

unread,
Jun 29, 2018, 5:45:13 PM6/29/18
to std-pr...@isocpp.org
It's not an oversight. It's a design decision that "class FloatImage;" forward-declares a class whose identity is known; its true name is FloatImage (in the same namespace). It is already known not to be identical with CudaImage<float>. Saying that it is, later, is contradicting what you told the compiler earlier.

As Nicol Bolas says, you need a third type of declaration to do what you want to do.

--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.



--
Brian Bi

Jake Arkinstall

unread,
Jun 29, 2018, 6:45:22 PM6/29/18
to std-pr...@isocpp.org
I had a similar refactoring once, and I approached it as follows. It might not apply, But as this does seem like an XY problem to me I thought it best to provide a potential solution.

The real problem is that you have code that depends on a class that doesn't exist yet. Templates are your friend here. When you need to use FloatImage, provide it as a template parameter. You then get the upshot of being able to use your code with something other than a FloatImage in the future.

You can also give CudaImage a "using value_type = T". That way any class which currently uses FloatImage can be given a template parameter T (which in your current code would be provided with FloatImage) can accept arguments of e.g. "typename T::value_type", and you aren't necessarily locked into using floats as arguments if someone comes along and wants a DoubleImage or something. If you want to restrict the types, concepts come to the rescue.

If you have an API to preserve, rename the old classes and functions as necessary, and expose the FloatImage-specialised versions under the original names. If this doesn't sound like a convincing approach, the Eigen library is a beautiful example (I'm not sure whether it is the result of a refactor or of some brilliant foresight).

Bengt Gustafsson

unread,
Jun 30, 2018, 2:49:52 AM6/30/18
to ISO C++ Standard - Future Proposals
I got one good answer: it is not possible due to name mangling having to know that FloatImage will resolve to a template instance. I used the proposed solution but it unfortunately had me change the forward declarations of FloatImage.

mihailn...@gmail.com

unread,
Jun 30, 2018, 3:52:31 AM6/30/18
to ISO C++ Standard - Future Proposals


On Saturday, June 30, 2018 at 9:49:52 AM UTC+3, Bengt Gustafsson wrote:
I got one good answer: it is not possible due to name mangling having to know that FloatImage will resolve to a template instance. I used the proposed solution but it unfortunately had me change the forward declarations of FloatImage.

 What solution did you use? 
Did you try the concrete class solution - it is not as naïve as it sounds, not as hard as it sounds (in C++11+) and has many protentional side benefits.

Otherwise the classical solution is not to forward declare a class but to have xxxfrw.h header instead, where you can put all "leakage of implementation detail". 

Bengt Gustafsson

unread,
Jun 30, 2018, 4:23:41 PM6/30/18
to ISO C++ Standard - Future Proposals, mihailn...@gmail.com
In my previous response I acknowedged the explanation that this has to do with name mangling, where ultimately the class name would be mangled according to its real identity, i.e. CudaImage<float> and thus that a forward declaration "class FloatImage;" would be natural to reject.

However, on second thought, FloatImage just as any class name is not an entity in itself that is known to the linker. And as you can't call methods on a class without seeing its full definition there is no risk that references to erroneously mangled method names sneak in to an object module compiled only seeing the forward declaration, right? With this in mind, is there a technical reason that 
my initial construct must be forbidden? I don't see that Nicol's explanation holds "FloatImage turns out eventually to be a type alias and not a class". But the very reason for having a type alias is to give a class another name, so why can't it be forward declared under this alternate name? Of course, if it was eventually a non-class type (or a variable or a function) the forward declaration would be erroneous, but that's the peril we have with allowing forward declarations at all... if they are in error there is no way to catch it... until modules, I guess.

I'm also considering this in the context of the proposed extension of alias declarations to other types of entities such as functions and variables. This has been touted as a way to avoid backwards compatibility issues, but if we can't extern declare functions and variables under their now deprecated names the utility of this feature is reduced as we would in some cases have to "tamper" with the untouchable code base that made the aliases necessary in the first place.

This said, it seems appropriate to ask if it would be possible to make the alias names known to the linker so that function calls to the alias name (allowed by a forward declaration of the function under its old name) can be resolved. I don't think this would be a problem with any contemporary linker/object file format, but maybe the language specification's concept of a linker does not include such facilities.

At least this consideration and a chosen strategy should be part of a generalized alias declaration proposal. Either such a strategy must mandate that the linker knows of the function/global variable under both names or it must show why this is not important even when claiming that the main motivation for the feature is being able to deprecate old names without having to change code using the entities under their old names. I guess this is a matter of having to compile with new headers or having to link with new libraries.




Den lördag 30 juni 2018 kl. 09:52:31 UTC+2 skrev mihailn...@gmail.com:


On Saturday, June 30, 2018 at 9:49:52 AM UTC+3, Bengt Gustafsson wrote:
I got one good answer: it is not possible due to name mangling having to know that FloatImage will resolve to a template instance. I used the proposed solution but it unfortunately had me change the forward declarations of FloatImage.

 What solution did you use? 
I did update the forward declaration to the proposed:


template<typename T> class CudaImage;
using FloatImage = CudaImage<float>;


 
Did you try the concrete class solution - it is not as naïve as it sounds, not as hard as it sounds (in C++11+) and has many protentional side benefits.
No, I didn't. This would have worked with some amount of boilerplate, but I wanted to see if I could do without this extraneous class.
 

Otherwise the classical solution is not to forward declare a class but to have xxxfrw.h header instead, where you can put all "leakage of implementation detail".
Sure, that would have been a possibility.
 

Nicol Bolas

unread,
Jun 30, 2018, 6:04:00 PM6/30/18
to ISO C++ Standard - Future Proposals, mihailn...@gmail.com


On Saturday, June 30, 2018 at 4:23:41 PM UTC-4, Bengt Gustafsson wrote:
In my previous response I acknowedged the explanation that this has to do with name mangling, where ultimately the class name would be mangled according to its real identity, i.e. CudaImage<float> and thus that a forward declaration "class FloatImage;" would be natural to reject.

However, on second thought, FloatImage just as any class name is not an entity in itself that is known to the linker. And as you can't call methods on a class without seeing its full definition there is no risk that references to erroneously mangled method names sneak in to an object module compiled only seeing the forward declaration, right?

With only a forward declaration, you cannot call members. Nor can you call non-member functions that take a reference to the forward declared type (because you would need a valid reference to a `FloatImage`). However, you can get a pointer to functions that take such types:

void func(const FloatImage &);

using fptr = void(*)(const FloatImage&);

fptr f
= func;

You can then take `f` and pass it to some API which itself can get `FloatImage` objects and pass them to the given function pointer. So the compiler needs to know how `f` works on something approaching an ABI level. And that means knowing if `FloatImage` is an actual type or a type alias (and if so, what it is an alias for).

Also, of course, you can call functions that take a pointer to `FloatImage` (you can only pass nullptr, of course). Since pointers are themselves typed, and linking needs to tell the difference, ABI name mangling needs to know if `FloatImage` is a type or a type alias.
 
With this in mind, is there a technical reason that 
my initial construct must be forbidden? I don't see that Nicol's explanation holds "FloatImage turns out eventually to be a type alias and not a class". But the very reason for having a type alias is to give a class another name, so why can't it be forward declared under this alternate name?

No, the reason to have a type alias is to give a type another name. `class FloatImage` will always be a class. `using FloatImage = ...;` is whatever `...` happens to be.

Your initial construct ought to be forbidden because it is incoherent. You declared that there is a class named `FloatImage`. You cannot later change your mind and decide that it is something else.

Reply all
Reply to author
Forward
0 new messages