When compiling this snipet in godbolt, most compilers generate two different get
methods (different symbol in the assembly window):
template<typename T>
struct Field { T impl; };
template<typename T>
using CurrentField = Field<T>;
template<template <typename> class F>
struct Encompassing { F<int> member; };
auto get(Encompassing<Field> const& instance)
{
return instance.member.impl;
}
auto get(Encompassing<CurrentField> const& instance)
{
return instance.member.impl;
}
I see CurrentField
in symbols even if it is an alias. Only gcc complains about redefinition (as expected).
C++ reference on type_alias says
It does not introduce a new type
so I think it is not supposed to behave like this, am I wrong?
Actually most compiler seems to behave like the alias template was substituted with a class Trait like
template<typename T>
struct CurrentField
{
alias type = Field<T> ;
};
Edit:
Here is a more representative example of What I try to achieve on Godbolt. Note that it compiles since there is a single source and no linking but the msvc assembly shows it generated both the pre-instantiated signatures and user calls signatures.
There are 4 parts:
1. a container library with multiple kind of templates like StackField
and HeapField
,
2. an utility library with member methods like size, with the field as template argument (like the second workaround you proposed),
3. implementations are hidden and pre-instantiated in the c++ for different fields
4. users link their application A and B against this library, using aliases like AField
and BField
. It works with gcc but link fails in msvc because the signature of my pre-instantiated implementations and the users calls don't match
It is true that an alias template does not introduce a new type. But your class template takes a template as parameter, not a type. Both Field
and CurrentField
are templates. So this boils down to the question of whether CurrentField
should count as the same template as Field
or as a separate template. This is CWG issue 1286. See also this thread. GCC follows the first interpretation, clang and MSVC follow the second…
A workaround would seem to be to break the direct mapping of CurrentField
to Field
by having it go through a helper template:
template <typename T>
struct CurrentFieldHelper { using type = Field<T>; };
template <typename T>
using CurrentField = typename CurrentFieldHelper<T>::type;
working example here
I'm afraid there is no way to achieve the opposite, i.e., make all compilers treat get(Encompassing<CurrentField> const &)
as the same function as get(Encompassing<Field> const &)
. It's hard to suggest a workaround for this particular problem without knowing more about your actual code. A simple solution that might or might not work for your actual code would be to make size
a function template that unpacks instance.member
and then forwards it to a common function that does the actual work:
template <template <typename> class F>
auto size(Encompassing<F> const& instance)
{
return size(instance.member);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With