Is it legal to partially specialise variadic template inner class with args from variadic template of an outer class

僤鯓⒐⒋嵵緔 提交于 2020-01-23 04:34:07

问题


Consider the code:

#include <iostream>

template <class... Ts>
struct outer {
   template <class... ITs>
   struct inner {
      static constexpr bool value = false;
   };

   template <class... ITs>
   struct inner<Ts..., ITs...> {   
      static constexpr bool value = true;
   };
};

int main() {
   std::cout << outer<int, float, double>::inner<int, float, double, int>::value << std::endl;
}

The code compiles with clang++ but not with g++ where it produces an error:

temp3.cc:11:11: error: parameter pack argument ‘Ts ...’ must be at the end of the template argument list

struct inner<Ts..., ITs...> {
       ^

As I've already established here partial specialisation of the inner class should be legit.

Edit: For completeness it is worth adding that clang for the above code warns that he might have a problem with deducing ITs parameters yet doing it without any problems...


回答1:


This is a gcc bug. This is a perfectly valid partial specialization:

template <class... ITs>
struct inner<Ts..., ITs...> {   
   static constexpr bool value = true;
};

Deduced template parameter packs must be last, and ITs... satisfies that. But Ts... isn't a pack that needs to be deduced here, it's just a specific parameter pack.

Furthermore, gcc compiles several equivalent formulations:

template <class... Ts>
struct X {
    template <class... Us>
    static void foo(Ts..., Us...) { }
};

int main() {
   X<int>::foo(1, 'c');
}

and:

template <class... Us>
struct A { };

template <class... Ts>
struct X {
    template <class... Us>
    static void foo(A<Ts..., Us...>) { }
};

int main() {
   X<int>::foo(A<int, char>{});
}

These are equivalently well-formed to your original example.




回答2:


Riding on W.F.'s answer, still keeping the same main as in the original question:

#include <iostream>

template <class... Ts>
struct pack { };

template <class... Ts>
class outer {
   template <class IT>
   struct _inner {
      static constexpr bool value = false;
   };

   template <class... ITs>
   struct _inner<pack<Ts..., ITs...>> {
      static constexpr bool value = true;
   };
public:
   template <class... ITs>
   struct inner {
      static constexpr bool value = _inner<pack<ITs...>>::value;
   };    
};

int main() {
   std::cout << outer<int, float, double>::inner<int, float, double, int>::value
             << std::endl;
}

It still produces a warning in clang, because the specialized version of _inner couldn't deduce ITs... apart from the list of Ts..., ITs... (in struct _inner<pack<Ts..., ITs...>>) -- however the code do not require ITs to be deduced separately from the list of Ts..., ITs..., so this should be ok.

In g++ it compiles without a warning.

Code: http://coliru.stacked-crooked.com/a/ae3b21dd847450b2

(For a solution without a warning also in clang: http://coliru.stacked-crooked.com/a/0c6c643c8ff5809e).




回答3:


Possible simple yet effective workaround inspired on Barry's answer:

#include <iostream>

template <class... Ts>
struct pack { };

template <class... Ts>
struct outer {
   template <class IT>
   struct inner {
      static constexpr bool value = false;
   };

   template <class... ITs>
   struct inner<pack<Ts..., ITs...>> {   
      static constexpr bool value = true;
   };
};

int main() {
   std::cout << outer<int, float, double>::inner<pack<int, float, double, int>>::value << std::endl;
}

(It still produces the warning in clang though)



来源:https://stackoverflow.com/questions/37767558/is-it-legal-to-partially-specialise-variadic-template-inner-class-with-args-from

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!