rmawatson
rmawatson

Reputation: 1943

class template, referring to its own type in definition

I am wondering which of visual studio 2017 and GCC is correct with the following situation with regards to the standard. The problem is that within the class template second, the identifier 'second' in visual studio always refers to the concrete type, but in gcc it appears to be context dependent.

The GCC example

template <typename...>
struct type_list{};

template<template <typename...> typename tmpl> 
struct tmpl_c
{
    template <typename...Ts> using type = tmpl<Ts...>;
};

template<typename> struct template_of;
template <template <typename...> typename tmpl, typename... Ts>
struct template_of<tmpl<Ts...>>{
    using type = tmpl_c<tmpl>; 

};


template <typename T>
struct first{};


template <typename T>
struct second
{
    // 'second' here refers to second<int>
    using test = second; 

    // 'second' here refers to the template second
    // is this due to the context? ie that the tmpl_c is expecting a template not a concrete type?
    using types = type_list<tmpl_c<first>, tmpl_c<second> >; 


    // second here refers to the concrete type 'second<int>'
    // this workaround is needed for visual studio as it seems second always 
    // refers to the concrete type, not the template.
    using types2 = type_list<tmpl_c<first>, typename template_of<second>::type >; 
};

Demo

And the Visual Studio example (only the different bits)

template <typename T>
struct second
{
    // 'second' here refers to second<int>
    using test = second; 

    // 'second' here refers to second<int>
    // this doesn't compile in visual studio as second<int> not the template.
    //using types = type_list<tmpl_c<first>, tmpl_c<second> >; 


    // second here refers to the concrete type 'second<int>'
    // this workaround is needed for visual studio as it seems second always 
    // refers to the concrete type, not the template.
    using types2 = type_list<tmpl_c<first>, typename template_of<second>::type >; 
};

Demo

As the template_of workaround appears to function correctly in both, its currently my only option.. but I would still like to know which is correct, or if there is another workaround..

fixed in visual studio 15.8 Preview 2

Upvotes: 5

Views: 1199

Answers (1)

songyuanyao
songyuanyao

Reputation: 173044

Gcc is correct. According to the rule of the usage of injected-class-name for class template, the injected-class-name can be used as also a template-name or a type-name.

(emphasis mine)

Like other classes, class templates have an injected-class-name. The injected-class-name can be used as a template-name or a type-name.

In the following cases, the injected-class-name is treated as a template-name of the class template itself:

  • it is followed by <
  • it is used as a template argument that corresponds to a template template parameter
  • it is the final identifier in the elaborated class specifier of a friend class template declaration.

Otherwise, it is treated as a type-name, and is equivalent to the template-name followed by the template-parameters of the class template enclosed in <>.

That means,

template <typename T>
struct second
{
    
    // second is same as second<T>
    using test = second; 

    // second is considered as a template-name
    // it's used as template argument for a template template parameter
    using types = type_list<tmpl_c<first>, tmpl_c<second> >; 

    // second is same as second<T>
    using types2 = type_list<tmpl_c<first>, typename template_of<second>::type >; 
};

Upvotes: 3

Related Questions