7

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

1 Answers1

2

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);
}
Michael Kenzel
  • 15,508
  • 2
  • 30
  • 39
  • Thank you, is there a simple workaround for this, having different symbols raises obvious link time problems. It is suggested to define " a “simple” alias, one in which the SFINAE conditions are the same as the referenced template and that uses all template parameters." but I don't get it – pierre cemontee Nov 28 '19 at 10:37
  • @pierrecemontee I added what seems to be a workaround to my answer above. – Michael Kenzel Nov 28 '19 at 10:46
  • Thank you but I was actually aiming at the opposite where all compiler reject the redefinition like gcc because I was hoping auto get(Encompassing const& instance) calls would gracefully link with the one and unique auto get(Encompassing const& instance) definition – pierre cemontee Nov 28 '19 at 11:09
  • @pierrecemontee Since some compilers will treat `Encompassing` as a separate, unrelated type, I can't think of a good solution for this in general. At least not if we assume that the real code you would like to apply this to is more complex than your example above. I've added one idea for a workaround to my answer, but I don't know if it would be applicable to your actual code or not… – Michael Kenzel Nov 28 '19 at 15:44
  • I have 1. a container library with multiple kind of templates like Field, 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 preinstantiated in the c++ for different fields 4. users link their application against this library, using aliases like the CurrentField one. It works with gcc but link fails in msvc because the signature of my pre-instantiated implementations and the users calls don't match – pierre cemontee Nov 28 '19 at 16:43