Template aliases conflicting types. g++ compiles successfully while clang fails





.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty{ height:90px;width:728px;box-sizing:border-box;
}







10















I encountered a very strange compiler error. For some reason the posted code does compile properly with g++ (7.3.0) while clang (7.0.0) fails:



../TemplateAlias/main.cpp:64:9: error: no matching function for call to 'freeFunc'
freeFunc(new Func, dummyField);
^~~~~~~~
../TemplateAlias/main.cpp:73:12: note: in instantiation of member function 'Helper<Traits<double, ConcreteData, ConcreteField> >::func' requested here
helper.func();
^
../TemplateAlias/main.cpp:21:13: note: candidate template ignored: deduced conflicting templates for parameter '' ('FieldData' vs. 'ConcreteData')
static void freeFunc(SomeFunc<T, FieldData>* func,
^


Both compiler options were set to -std=c++14



template<typename T>
struct ConcreteData
{
T data;
};

template<typename T, template<typename U> class FieldData>
struct ConcreteField
{
FieldData<T> someMember;
};

template<typename T, template<typename U> class FieldData>
struct SomeFunc
{

};


template<typename T, template<typename U> class FieldData>
static void freeFunc(SomeFunc<T, FieldData>* func,
ConcreteField<T, FieldData>& field)
{
// apply the func on data
(void)field; // silence compiler warning
delete func;
}


template<
typename ScalarType,
template<typename U> class FieldDataType,
template<typename U, template <typename X> class Data> class FieldType
>
struct Traits
{
using Scalar = ScalarType;

template<typename T>
using FieldData = FieldDataType<T>;

using Field = FieldType<Scalar, FieldDataType>; // fails with clang only
// using Field = FieldType<Scalar, FieldData>; // using this line helps clang

};

template<typename Traits>
struct Helper
{
// alias all types given by trait for easier access
using Scalar = typename Traits::Scalar;
using Field = typename Traits::Field;

template<typename U>
using DataAlias = typename Traits::template FieldData<U>;

void func()
{
// using Func = SomeFunc<Scalar, DataAlias>; // this line is intended, but fails with both GCC and clang
using Func = SomeFunc<Scalar, Traits::template FieldData>; // compiles only with GCC, fails with clang


Field dummyField;
freeFunc(new Func, dummyField);
}
};


int main()
{
using ConcreteTraits = Traits<double, ConcreteData, ConcreteField>;
Helper<ConcreteTraits> helper;
helper.func();

return 0;
}


According to cppreference.com:




A type alias declaration introduces a name which can be used as a
synonym for the type denoted by type-id. It does not introduce a new
type and it cannot change the meaning of an existing type name. There
is no difference between a type alias declaration and typedef
declaration. This declaration may appear in block scope, class scope,
or namespace scope.




and




Alias templates are never deduced by template argument deduction when
deducing a template template parameter.




In my understanding both types (ConcreteData and FieldData) should be equivalent. Why is clang failing in this condition and why do both compiler fail when using the "second stage" alias? Which compiler is right according to the C++ standard? Is it a compiler bug or a subtle ambiguous interpretation of the C++14 standard?










share|improve this question




















  • 3





    Here is a more minimal MCVE: godbolt.org/z/nSFQ3n

    – Oktalist
    Nov 26 '18 at 18:14






  • 1





    "A type alias declaration introduces a name..." refers to a non-templated using. The more relevant cppref quote is "An alias template is a template which, when specialized, is equivalent to the result of substituting the template arguments of the alias template for the template parameters in the type-id". (Bold added by me.)

    – Oktalist
    Nov 26 '18 at 18:16




















10















I encountered a very strange compiler error. For some reason the posted code does compile properly with g++ (7.3.0) while clang (7.0.0) fails:



../TemplateAlias/main.cpp:64:9: error: no matching function for call to 'freeFunc'
freeFunc(new Func, dummyField);
^~~~~~~~
../TemplateAlias/main.cpp:73:12: note: in instantiation of member function 'Helper<Traits<double, ConcreteData, ConcreteField> >::func' requested here
helper.func();
^
../TemplateAlias/main.cpp:21:13: note: candidate template ignored: deduced conflicting templates for parameter '' ('FieldData' vs. 'ConcreteData')
static void freeFunc(SomeFunc<T, FieldData>* func,
^


Both compiler options were set to -std=c++14



template<typename T>
struct ConcreteData
{
T data;
};

template<typename T, template<typename U> class FieldData>
struct ConcreteField
{
FieldData<T> someMember;
};

template<typename T, template<typename U> class FieldData>
struct SomeFunc
{

};


template<typename T, template<typename U> class FieldData>
static void freeFunc(SomeFunc<T, FieldData>* func,
ConcreteField<T, FieldData>& field)
{
// apply the func on data
(void)field; // silence compiler warning
delete func;
}


template<
typename ScalarType,
template<typename U> class FieldDataType,
template<typename U, template <typename X> class Data> class FieldType
>
struct Traits
{
using Scalar = ScalarType;

template<typename T>
using FieldData = FieldDataType<T>;

using Field = FieldType<Scalar, FieldDataType>; // fails with clang only
// using Field = FieldType<Scalar, FieldData>; // using this line helps clang

};

template<typename Traits>
struct Helper
{
// alias all types given by trait for easier access
using Scalar = typename Traits::Scalar;
using Field = typename Traits::Field;

template<typename U>
using DataAlias = typename Traits::template FieldData<U>;

void func()
{
// using Func = SomeFunc<Scalar, DataAlias>; // this line is intended, but fails with both GCC and clang
using Func = SomeFunc<Scalar, Traits::template FieldData>; // compiles only with GCC, fails with clang


Field dummyField;
freeFunc(new Func, dummyField);
}
};


int main()
{
using ConcreteTraits = Traits<double, ConcreteData, ConcreteField>;
Helper<ConcreteTraits> helper;
helper.func();

return 0;
}


According to cppreference.com:




A type alias declaration introduces a name which can be used as a
synonym for the type denoted by type-id. It does not introduce a new
type and it cannot change the meaning of an existing type name. There
is no difference between a type alias declaration and typedef
declaration. This declaration may appear in block scope, class scope,
or namespace scope.




and




Alias templates are never deduced by template argument deduction when
deducing a template template parameter.




In my understanding both types (ConcreteData and FieldData) should be equivalent. Why is clang failing in this condition and why do both compiler fail when using the "second stage" alias? Which compiler is right according to the C++ standard? Is it a compiler bug or a subtle ambiguous interpretation of the C++14 standard?










share|improve this question




















  • 3





    Here is a more minimal MCVE: godbolt.org/z/nSFQ3n

    – Oktalist
    Nov 26 '18 at 18:14






  • 1





    "A type alias declaration introduces a name..." refers to a non-templated using. The more relevant cppref quote is "An alias template is a template which, when specialized, is equivalent to the result of substituting the template arguments of the alias template for the template parameters in the type-id". (Bold added by me.)

    – Oktalist
    Nov 26 '18 at 18:16
















10












10








10


1






I encountered a very strange compiler error. For some reason the posted code does compile properly with g++ (7.3.0) while clang (7.0.0) fails:



../TemplateAlias/main.cpp:64:9: error: no matching function for call to 'freeFunc'
freeFunc(new Func, dummyField);
^~~~~~~~
../TemplateAlias/main.cpp:73:12: note: in instantiation of member function 'Helper<Traits<double, ConcreteData, ConcreteField> >::func' requested here
helper.func();
^
../TemplateAlias/main.cpp:21:13: note: candidate template ignored: deduced conflicting templates for parameter '' ('FieldData' vs. 'ConcreteData')
static void freeFunc(SomeFunc<T, FieldData>* func,
^


Both compiler options were set to -std=c++14



template<typename T>
struct ConcreteData
{
T data;
};

template<typename T, template<typename U> class FieldData>
struct ConcreteField
{
FieldData<T> someMember;
};

template<typename T, template<typename U> class FieldData>
struct SomeFunc
{

};


template<typename T, template<typename U> class FieldData>
static void freeFunc(SomeFunc<T, FieldData>* func,
ConcreteField<T, FieldData>& field)
{
// apply the func on data
(void)field; // silence compiler warning
delete func;
}


template<
typename ScalarType,
template<typename U> class FieldDataType,
template<typename U, template <typename X> class Data> class FieldType
>
struct Traits
{
using Scalar = ScalarType;

template<typename T>
using FieldData = FieldDataType<T>;

using Field = FieldType<Scalar, FieldDataType>; // fails with clang only
// using Field = FieldType<Scalar, FieldData>; // using this line helps clang

};

template<typename Traits>
struct Helper
{
// alias all types given by trait for easier access
using Scalar = typename Traits::Scalar;
using Field = typename Traits::Field;

template<typename U>
using DataAlias = typename Traits::template FieldData<U>;

void func()
{
// using Func = SomeFunc<Scalar, DataAlias>; // this line is intended, but fails with both GCC and clang
using Func = SomeFunc<Scalar, Traits::template FieldData>; // compiles only with GCC, fails with clang


Field dummyField;
freeFunc(new Func, dummyField);
}
};


int main()
{
using ConcreteTraits = Traits<double, ConcreteData, ConcreteField>;
Helper<ConcreteTraits> helper;
helper.func();

return 0;
}


According to cppreference.com:




A type alias declaration introduces a name which can be used as a
synonym for the type denoted by type-id. It does not introduce a new
type and it cannot change the meaning of an existing type name. There
is no difference between a type alias declaration and typedef
declaration. This declaration may appear in block scope, class scope,
or namespace scope.




and




Alias templates are never deduced by template argument deduction when
deducing a template template parameter.




In my understanding both types (ConcreteData and FieldData) should be equivalent. Why is clang failing in this condition and why do both compiler fail when using the "second stage" alias? Which compiler is right according to the C++ standard? Is it a compiler bug or a subtle ambiguous interpretation of the C++14 standard?










share|improve this question
















I encountered a very strange compiler error. For some reason the posted code does compile properly with g++ (7.3.0) while clang (7.0.0) fails:



../TemplateAlias/main.cpp:64:9: error: no matching function for call to 'freeFunc'
freeFunc(new Func, dummyField);
^~~~~~~~
../TemplateAlias/main.cpp:73:12: note: in instantiation of member function 'Helper<Traits<double, ConcreteData, ConcreteField> >::func' requested here
helper.func();
^
../TemplateAlias/main.cpp:21:13: note: candidate template ignored: deduced conflicting templates for parameter '' ('FieldData' vs. 'ConcreteData')
static void freeFunc(SomeFunc<T, FieldData>* func,
^


Both compiler options were set to -std=c++14



template<typename T>
struct ConcreteData
{
T data;
};

template<typename T, template<typename U> class FieldData>
struct ConcreteField
{
FieldData<T> someMember;
};

template<typename T, template<typename U> class FieldData>
struct SomeFunc
{

};


template<typename T, template<typename U> class FieldData>
static void freeFunc(SomeFunc<T, FieldData>* func,
ConcreteField<T, FieldData>& field)
{
// apply the func on data
(void)field; // silence compiler warning
delete func;
}


template<
typename ScalarType,
template<typename U> class FieldDataType,
template<typename U, template <typename X> class Data> class FieldType
>
struct Traits
{
using Scalar = ScalarType;

template<typename T>
using FieldData = FieldDataType<T>;

using Field = FieldType<Scalar, FieldDataType>; // fails with clang only
// using Field = FieldType<Scalar, FieldData>; // using this line helps clang

};

template<typename Traits>
struct Helper
{
// alias all types given by trait for easier access
using Scalar = typename Traits::Scalar;
using Field = typename Traits::Field;

template<typename U>
using DataAlias = typename Traits::template FieldData<U>;

void func()
{
// using Func = SomeFunc<Scalar, DataAlias>; // this line is intended, but fails with both GCC and clang
using Func = SomeFunc<Scalar, Traits::template FieldData>; // compiles only with GCC, fails with clang


Field dummyField;
freeFunc(new Func, dummyField);
}
};


int main()
{
using ConcreteTraits = Traits<double, ConcreteData, ConcreteField>;
Helper<ConcreteTraits> helper;
helper.func();

return 0;
}


According to cppreference.com:




A type alias declaration introduces a name which can be used as a
synonym for the type denoted by type-id. It does not introduce a new
type and it cannot change the meaning of an existing type name. There
is no difference between a type alias declaration and typedef
declaration. This declaration may appear in block scope, class scope,
or namespace scope.




and




Alias templates are never deduced by template argument deduction when
deducing a template template parameter.




In my understanding both types (ConcreteData and FieldData) should be equivalent. Why is clang failing in this condition and why do both compiler fail when using the "second stage" alias? Which compiler is right according to the C++ standard? Is it a compiler bug or a subtle ambiguous interpretation of the C++14 standard?







c++ templates c++14 typetraits template-aliases






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 26 '18 at 17:49







srohmen

















asked Nov 26 '18 at 16:20









srohmensrohmen

535




535








  • 3





    Here is a more minimal MCVE: godbolt.org/z/nSFQ3n

    – Oktalist
    Nov 26 '18 at 18:14






  • 1





    "A type alias declaration introduces a name..." refers to a non-templated using. The more relevant cppref quote is "An alias template is a template which, when specialized, is equivalent to the result of substituting the template arguments of the alias template for the template parameters in the type-id". (Bold added by me.)

    – Oktalist
    Nov 26 '18 at 18:16
















  • 3





    Here is a more minimal MCVE: godbolt.org/z/nSFQ3n

    – Oktalist
    Nov 26 '18 at 18:14






  • 1





    "A type alias declaration introduces a name..." refers to a non-templated using. The more relevant cppref quote is "An alias template is a template which, when specialized, is equivalent to the result of substituting the template arguments of the alias template for the template parameters in the type-id". (Bold added by me.)

    – Oktalist
    Nov 26 '18 at 18:16










3




3





Here is a more minimal MCVE: godbolt.org/z/nSFQ3n

– Oktalist
Nov 26 '18 at 18:14





Here is a more minimal MCVE: godbolt.org/z/nSFQ3n

– Oktalist
Nov 26 '18 at 18:14




1




1





"A type alias declaration introduces a name..." refers to a non-templated using. The more relevant cppref quote is "An alias template is a template which, when specialized, is equivalent to the result of substituting the template arguments of the alias template for the template parameters in the type-id". (Bold added by me.)

– Oktalist
Nov 26 '18 at 18:16







"A type alias declaration introduces a name..." refers to a non-templated using. The more relevant cppref quote is "An alias template is a template which, when specialized, is equivalent to the result of substituting the template arguments of the alias template for the template parameters in the type-id". (Bold added by me.)

– Oktalist
Nov 26 '18 at 18:16














1 Answer
1






active

oldest

votes


















7














Borrowing the minimal example of @Oktalist.



template <typename>
class T {};

template <typename _>
using U = T<_>;

template <template <typename> class X>
void f(A<X>, A<X>) {}


if you replace f by:



template <template <typename> class X, template <typename> class Y>
void f(A<X>, A<Y>) {}


the code no longer fail to compile. You can see that the problem is about equivalence of template parameters X and Y, they are deduced to different types.



The equivalence of types produced by alias template are only considered when referring to specialization of the alias, as is specified on [temp.alias]/2:




When a template-id refers to the specialization of an alias template, it is equivalent to the associated type obtained by substitution of its template-arguments for the template-parameters in the type-id of the alias template




Using this rule and the rules for equivalence [temp.type]/1:



T<int> and U<int> are equivalent, so are X<T<int>> and Z<U<int>>, but this rule doesn't extend to the alias template U being equivalent to the class template T (by themselves, they aren't specializations).



This is the same scenario for the alias FieldData and the class template ConcreteData.



There are in fact two defect report, CWG-1286 and CWG-1244 that propose the equivalence extension for alias templates.






share|improve this answer


























    Your Answer






    StackExchange.ifUsing("editor", function () {
    StackExchange.using("externalEditor", function () {
    StackExchange.using("snippets", function () {
    StackExchange.snippets.init();
    });
    });
    }, "code-snippets");

    StackExchange.ready(function() {
    var channelOptions = {
    tags: "".split(" "),
    id: "1"
    };
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function() {
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled) {
    StackExchange.using("snippets", function() {
    createEditor();
    });
    }
    else {
    createEditor();
    }
    });

    function createEditor() {
    StackExchange.prepareEditor({
    heartbeatType: 'answer',
    autoActivateHeartbeat: false,
    convertImagesToLinks: true,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: 10,
    bindNavPrevention: true,
    postfix: "",
    imageUploader: {
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    },
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    });


    }
    });














    draft saved

    draft discarded


















    StackExchange.ready(
    function () {
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53485197%2ftemplate-aliases-conflicting-types-g-compiles-successfully-while-clang-fails%23new-answer', 'question_page');
    }
    );

    Post as a guest















    Required, but never shown

























    1 Answer
    1






    active

    oldest

    votes








    1 Answer
    1






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes









    7














    Borrowing the minimal example of @Oktalist.



    template <typename>
    class T {};

    template <typename _>
    using U = T<_>;

    template <template <typename> class X>
    void f(A<X>, A<X>) {}


    if you replace f by:



    template <template <typename> class X, template <typename> class Y>
    void f(A<X>, A<Y>) {}


    the code no longer fail to compile. You can see that the problem is about equivalence of template parameters X and Y, they are deduced to different types.



    The equivalence of types produced by alias template are only considered when referring to specialization of the alias, as is specified on [temp.alias]/2:




    When a template-id refers to the specialization of an alias template, it is equivalent to the associated type obtained by substitution of its template-arguments for the template-parameters in the type-id of the alias template




    Using this rule and the rules for equivalence [temp.type]/1:



    T<int> and U<int> are equivalent, so are X<T<int>> and Z<U<int>>, but this rule doesn't extend to the alias template U being equivalent to the class template T (by themselves, they aren't specializations).



    This is the same scenario for the alias FieldData and the class template ConcreteData.



    There are in fact two defect report, CWG-1286 and CWG-1244 that propose the equivalence extension for alias templates.






    share|improve this answer






























      7














      Borrowing the minimal example of @Oktalist.



      template <typename>
      class T {};

      template <typename _>
      using U = T<_>;

      template <template <typename> class X>
      void f(A<X>, A<X>) {}


      if you replace f by:



      template <template <typename> class X, template <typename> class Y>
      void f(A<X>, A<Y>) {}


      the code no longer fail to compile. You can see that the problem is about equivalence of template parameters X and Y, they are deduced to different types.



      The equivalence of types produced by alias template are only considered when referring to specialization of the alias, as is specified on [temp.alias]/2:




      When a template-id refers to the specialization of an alias template, it is equivalent to the associated type obtained by substitution of its template-arguments for the template-parameters in the type-id of the alias template




      Using this rule and the rules for equivalence [temp.type]/1:



      T<int> and U<int> are equivalent, so are X<T<int>> and Z<U<int>>, but this rule doesn't extend to the alias template U being equivalent to the class template T (by themselves, they aren't specializations).



      This is the same scenario for the alias FieldData and the class template ConcreteData.



      There are in fact two defect report, CWG-1286 and CWG-1244 that propose the equivalence extension for alias templates.






      share|improve this answer




























        7












        7








        7







        Borrowing the minimal example of @Oktalist.



        template <typename>
        class T {};

        template <typename _>
        using U = T<_>;

        template <template <typename> class X>
        void f(A<X>, A<X>) {}


        if you replace f by:



        template <template <typename> class X, template <typename> class Y>
        void f(A<X>, A<Y>) {}


        the code no longer fail to compile. You can see that the problem is about equivalence of template parameters X and Y, they are deduced to different types.



        The equivalence of types produced by alias template are only considered when referring to specialization of the alias, as is specified on [temp.alias]/2:




        When a template-id refers to the specialization of an alias template, it is equivalent to the associated type obtained by substitution of its template-arguments for the template-parameters in the type-id of the alias template




        Using this rule and the rules for equivalence [temp.type]/1:



        T<int> and U<int> are equivalent, so are X<T<int>> and Z<U<int>>, but this rule doesn't extend to the alias template U being equivalent to the class template T (by themselves, they aren't specializations).



        This is the same scenario for the alias FieldData and the class template ConcreteData.



        There are in fact two defect report, CWG-1286 and CWG-1244 that propose the equivalence extension for alias templates.






        share|improve this answer















        Borrowing the minimal example of @Oktalist.



        template <typename>
        class T {};

        template <typename _>
        using U = T<_>;

        template <template <typename> class X>
        void f(A<X>, A<X>) {}


        if you replace f by:



        template <template <typename> class X, template <typename> class Y>
        void f(A<X>, A<Y>) {}


        the code no longer fail to compile. You can see that the problem is about equivalence of template parameters X and Y, they are deduced to different types.



        The equivalence of types produced by alias template are only considered when referring to specialization of the alias, as is specified on [temp.alias]/2:




        When a template-id refers to the specialization of an alias template, it is equivalent to the associated type obtained by substitution of its template-arguments for the template-parameters in the type-id of the alias template




        Using this rule and the rules for equivalence [temp.type]/1:



        T<int> and U<int> are equivalent, so are X<T<int>> and Z<U<int>>, but this rule doesn't extend to the alias template U being equivalent to the class template T (by themselves, they aren't specializations).



        This is the same scenario for the alias FieldData and the class template ConcreteData.



        There are in fact two defect report, CWG-1286 and CWG-1244 that propose the equivalence extension for alias templates.







        share|improve this answer














        share|improve this answer



        share|improve this answer








        edited Nov 26 '18 at 20:26

























        answered Nov 26 '18 at 19:48









        JansJans

        9,17422735




        9,17422735
































            draft saved

            draft discarded




















































            Thanks for contributing an answer to Stack Overflow!


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.




            draft saved


            draft discarded














            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53485197%2ftemplate-aliases-conflicting-types-g-compiles-successfully-while-clang-fails%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            Popular posts from this blog

            Wiesbaden

            Marschland

            Dieringhausen