THIRD EDIT: Today I received email from VS support stating that it's know issue, which has been fixed in VS2017. So, I will stick to my workaround for the time being.
I'm working on event system and I wanted to use some metaprogramming to save on typing. Here's the code:
struct foo { using type = int; };
struct bar { using type = char; };
template<typename... Types> struct TypeList {};
//This and getScript are simplified, in reality the involve messier templates...
template<typename ReturnType>
struct ReqBuilder
{
using proxy = ReturnType(*)(void*, bool);
// ...
};
template<typename scriptType>
using getScript = ReqBuilder<scriptType>;
template<typename List, template<typename> typename Wrap> struct WrapTypeList_impl {};
template<typename...Ts, template<typename> typename Wrap> struct WrapTypeList_impl<TypeList<Ts...>, Wrap>
{
using type = TypeList<Wrap< Ts>...>;
};
template<typename List, template<typename> typename Wrap>
using WrapTypeList = typename WrapTypeList_impl<List, Wrap>::type;
//Use typedef
using list1 = TypeList<getScript<foo>, getScript<bar>>;
//Write it manually
using list2 = TypeList<ReqBuilder<foo>, ReqBuilder<bar>>;
//Use wrapper to wrap each element of typelist
using list3 = WrapTypeList<TypeList<foo, bar>, getScript>;
Goal is to have all three list result in the same TypeList. This code is simplified, in reality getScript does more work and it's actually useful. Same for ReqBuilder which contains more typedefs. So, list3 should be much easier to write than either list1 or list2.
There are more getScript, ReqBuilder-like classes and flattening of nested typelists. Which allows to easily write list of all possible events even with complex types.
This code above works - all lists are equal. Problem is when specialize (and I want to) on nested type:
template<typename scriptType>
using getScript = ReqBuilder<typename scriptType::type>;
Then I get following error atWrapTypeList_impl::type=...
line:
Error C2938 'getScript<unknown-type>' : Failed to specialize alias template
I really have no idea what that even means, but it probably has something to do with pack expansion, because when I add this specialization:
template<typename A,typename B, template<typename> typename Wrap>
struct WrapTypeList_impl<TypeList<A,B>, Wrap>
{
using type = TypeList<Wrap<A>,Wrap<B>>;
};
Then it works even with nested types. So, does anyone have any idea how to get this working please?
I'm using Visual studio 2015 Comunnity edition. And even more puzzling thing is that using intellisense - hovering over lists shows they resolved in same TypeList, even std::is_same<list1,list3>::value
shows true
on hover. But compiler for some reason disagrees.
EDIT: ADDED FULL CODE THAT CAUSES SAID ERROR
#include <algorithm>
struct foo { using type = int; };
struct bar { using type = char; };
template<typename... Types> struct TypeList {};
//This and getScript are simplified, in reality the involve messier templates...
template<typename ReturnType>
struct ReqBuilder
{
using proxy = ReturnType(*)(void*, bool);
// ...
};
template<typename scriptType>
using getScript = ReqBuilder<typename scriptType::type>;
template<typename List, template<typename> typename Wrap> struct WrapTypeList_impl {};
template<typename...Ts, template<typename> typename Wrap> struct WrapTypeList_impl<TypeList<Ts...>, Wrap>
{
using type = TypeList<Wrap<Ts>...>;
};
template<typename List, template<typename> typename Wrap>
using WrapTypeList = typename WrapTypeList_impl<List, Wrap>::type;
using list1 = TypeList<getScript<foo>, getScript<bar>>;
using list2 = TypeList<ReqBuilder<typename foo::type>, ReqBuilder<typename bar::type>>;
using list3 = WrapTypeList<TypeList<foo, bar>, getScript>;
int main()
{
constexpr bool A = std::is_same<list1, list2>::value;
constexpr bool B = std::is_same<list1, list3>::value;
constexpr bool C = std::is_same<list2, list3>::value;
static_assert(A, "list1 != list2");
static_assert(B, "list1 != list3");
static_assert(C, "list2 != list3");
}
Erorrs:
1> Main.cpp
1>Main.cpp(23): error C2938: 'getScript<unknown-type>' : Failed to specialize alias template
1> Main.cpp(31): note: see reference to class template instantiation 'WrapTypeList_impl<TypeList<foo,bar>,getScript>' being compiled
1>Main.cpp(23): error C3546: '...': there are no parameter packs available to expand
1>Main.cpp(36): error C3203: 'TypeList': unspecialized class template can't be used as a template argument for template parameter '_Ty2', expected a real type
1>Main.cpp(37): error C3203: 'TypeList': unspecialized class template can't be used as a template argument for template parameter '_Ty2', expected a real type
1>Main.cpp(40): error C2338: list1 != list3
1>Main.cpp(41): error C2338: list2 != list3
Adding said A,B specialization for Wrapper after variadic one solves it.
SECOND EDIT - FOUND WORKAROUND
So, original problem still exists, but after bit of trial and error, I tried to replace dependent type with separate struct, which seems to solve the problem:
template<typename scriptType>
struct getScript_impl
{
using type = ReqBuilder<typename scriptType::type>;
};
template<typename scriptType>
using getScript = typename getScript_impl<scriptType>::type;
/* REPLACING THIS WITH CODE ABOVE
template<typename scriptType>
using getScript = ReqBuilder<typename scriptType::type>;
*/
Full code, which works in VS2015 for me: http://ideone.com/kGT4qM
As I briefly stated in my comment, I think the issue is that alias template using dependent type as another template argument does not instantiate that template or something like that.(I'm not a template wizard, I really just poke at them, and see how they respond).
I've found few links that are related to this issue, but didn't have time to look at them yet(link1; link2; link3).