Why can't Visual C++ partially specialize a class inside a template? - c++

When I try to run this code in Visual C++ (2015)
template<int V>
struct outer
{
template<int U, bool>
struct inner;
};
template<int V>
template<bool B>
struct outer<V>::inner<V, B> { enum { value = 0 }; };
int main()
{
return outer<1>::inner<1, false>::value;
}
I get the error
Temp.cpp(13): error C2027: use of undefined type 'outer<1>::inner<1,false>'
Temp.cpp(13): note: see declaration of 'outer<1>::inner<1,false>'
Temp.cpp(13): error C2065: 'value': undeclared identifier
However, it compiles and runs fine on GCC and Clang.
Three questions:
If the partial specialization isn't partially specializing, what is it doing?
Why does this happen? Is it a bug, or is there really a problem with this code?
Is there a workaround that lets you still use the inner template class inside the inner template class, or is the only solution to move the template arguments outside?

This is a known limitation in the implementation of C++ in Visual C++ 2015 (one of many).
This does work in Visual C++ 2017, so a version upgrade may be necessary.

Related

Why can't I declare a static constexpr variable in a template class using msvc?

I have a template class, and I want to declare a static constexpr variable of the same type of the class. With gnu compiler it works just fine, but with
Microsoft Visual Studio, it won't compile. Am I doing something wrong and it is jest the gnu compiler that is very kind with me, or is it the Microsoft compiler that is at fault?
I know I can fix it changing the variable for a function that does the same thing, but I'm curious.
template <typename T>
constexpr T One() noexcept { return static_cast<T>( 1 ); }
template <typename T>
struct Test {
T val;
static constexpr Test example{ One<T>() }; // compiles only with gnu
static constexpr Test Example() { return Test{ One<T>() }; } // compiles with both gnu and microsoft
};
The given error (Visal Studio 2017) is :
error C2017 : use of undefined type 'Test'
Before the last } your Test template type is incomplete.
Here’s very similar question which adds third compiler. As you see, the answer says VC++ and clang respect the standard, gcc doesn’t.

SFINAE std::enable_if fails in visual studio 2015

I was trying to port an open sourced project from linux to window, there are some code that compiles perfectly in linux using either g++ or clang++, but I cannot compile it under MSVC 2015, could anyone tell me how to fix it or how to get around it? I appreciate very much for your help!!!
Here is the code (I have simplified it so you can focus on the key stuff)
template <typename T, class IsMatch = void>
class vc_hashkey
{
public:
static constexpr bool holds_value() { return false; }
};
template <typename T>
class vc_hashkey<T, typename std::enable_if<std::is_integral<T>::value>::type>
{
public:
static constexpr bool holds_value() { return true; }
};
template <typename T, class IsMatch = void>
class vc_hashkey_and_value
{
};
template <typename T>
class vc_hashkey_and_value<T, typename std::enable_if<vc_hashkey<T>::holds_value()>::type>
{
};
That's it, I did not even used these code pieces in my main function. When I tried to compile, the msvc 2015 update RC1 compiler gives me compile errors on the partial specialization of the class vc_hashkey_and_value, saying:
C2039 'type': is not a member of 'std::enable_if<'false, void>'
C2146 syntax error: missing '>' before identifier 'type'
C2064 term does not evaluate to a function taking 0 arguments
Is this a MSVC compiler error, thanks for helping me!!!

How to specialize a class template for a tuple by using variadic template arguments?

How to specialize a class template for a tuple? I try the following but failed. I am using VC Compiler Nov 2012 CTP which support variadic template arguments.
template<class T>
struct A
{
void f() {}
};
template<class... Args>
struct A<tuple<Args...>>
{
void g() {}
};
I try
A<tuple<int, float>> a;
a.g(); // error error C2039: 'g' : is not a member of
//'A<std::tuple<int,float,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil>>
Thanks to yngum. It might be a VC compiler bug. If so, how to work around?
This is a compiler-bug. As a work-around, simply add the 2-parameter specialization
template<class Arg0, class Arg1>
struct A< std::tuple<Arg0, Arg1> >
{
void g() {}
};
Live Example.
Yes, you'll have to do this for as many parameters as your code is likely to use. Or, you could try VS 2013 RC which is likely to have fixed this bug.
Update: I now see that you asked a separate question about the work-arounds. The same solution was posted there.

Template trouble with Visual Studio 2010

With GCC in Linux, this following code compiles well. But with visual studio, an error C2893 occurs. Do you have any ideas ?
struct A
{
};
struct B
{
typedef A Data;
};
struct C
{
typedef B Data;
};
template<typename Type>
typename Type::Data::Data test( Type in)
{
return Type::Data::Data();
}
int main(){
C c;
C::Data::Data a;//works
test(c);//error C2893: The specialization of the function template 'Type :: Data :: {ctor} test (Type)' failed
}
Thanks a lot
Solution by Avakar: because Data::Data refers to the constructor of the type Data, you have to use this idiom:
typename identity<typename Type::Data>::type::Data
The compiler failed to specialize a function template. There can be many causes for this error. In general, the way to resolve a C2893 error is to review the function's signature and make sure you can instantiate every type.

g++ template bug or VC too liberal?

I am writing a heavily templated piece of code in C++. It worked perfectly in VS 2005 but when I try to compile it in g++ I get some really strange errors.
The essential piece of code (simplified to a minimum, doesn't compile either) is as follows:
template <class Actual>
class Generic
{
public:
typedef Actual ThisType;
};
template <class Actual>
class Intermediate : public Generic<Actual>
{
};
template <class Q>
class Derived : public Intermediate<Derived<Q> >
{
public:
void FooBar()
{
ThisType q;
}
};
The error is:
"'ThisType' was not declared in this scope"
in the line where 'q' is being declared.
Curiously, everything works fine when Derived is not a template but a plain class. Why would compiler look into template function implementation before it is even instantiated? I know that VC++ checks far too little when compiling templates (unused templates can even contain syntactically incorrect code) - but isn't g++ checking too much here? I tried adding a typename keyword with little hope and it fails too. Is there any way to get ThisType to work as expected? I dread the thought of adding it manually to every single derived class - it's cumbersome, redundant, inelegant and error-inducing.
Best regards,
MZ
Unqualified names are not looked up in dependent base classes (your base class depends on template parameter Q). Qualify that name, and it will work.
typename Derived::ThisType q;
Trust in Comeau online compiler!
Comeau C/C++ 4.3.10.1 (Oct 6 2008 11:28:09) for ONLINE_EVALUATION_BETA2
Copyright 1988-2008 Comeau Computing. All rights reserved.
MODE:strict errors C++ C++0x_extensions
"ComeauTest.c", line 19: error: identifier "ThisType" is undefined
ThisType q;
^
"ComeauTest.c", line 19: error: expected a ";" (perhaps on the previous statement)
ThisType q;
^
2 errors detected in the compilation of "ComeauTest.c".
Dependent type names from inherited classes are not taken into account, you might try to explicitly request ThisType:
typename Intermediate<Derived<Q> >::ThisType q;
The code is indeed ill-formed. When base class depends on template parameter it is not considered by name lookup when looking for unqualified names.
In your case unqualified name ThisType will not be looked up in base class Intermediate<Derived<Q> > because your base class depends on template parameter Q.
You are missing a typename
template <class Actual> class Generic { public:
typedef Actual ThisType; };
template <class Actual> class Intermediate : public Generic<Actual> { };
template <class Q> class Derived : public Intermediate<Derived<Q> > { public:
void FooBar()
{
typename Derived::ThisType q;
return *this;
} };
int main(){}