Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Class templates alias should not be visible in generated symbols, do they?

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

like image 677
pierre cemontee Avatar asked Nov 28 '19 10:11

pierre cemontee


Video Answer


1 Answers

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);
}
like image 132
Michael Kenzel Avatar answered Oct 18 '22 15:10

Michael Kenzel