问题
In the following example, I have an object class which defines a reference class. Both accept mutability as a template argument. In the case that 'obj' is 'Const', I want to disallow a reference of type 'Non_Const'. The example produces the ambiguous message, "An internal error has occurred in the compiler," in Visual C++ 2012. Should this compile? If not, why, and is there another way to accomplish the same thing?
enum Mutability {Const, Non_Const};
template <typename T, Mutability U>
class Obj
{
public:
template <Mutability V>
class Ref
{
public:
Ref() {}
friend class Obj;
};
Obj() {}
};
template <typename T>
class Obj<T, Const>::template Ref<Non_Const>
{
private:
Ref() {}
}; //error C1001: An internal error has occurred in the compiler
int main()
{
Obj<int, Const>::Ref<Non_Const> test;
}
回答1:
If what you're seeking to do is disallow instantiation of a V
as Ref
when U
is Mutability::const
, we can enforce this concept using some template trickery:
(someone smarter than me can probably make this simpler)
enum class Mutability {Const, Non_Const};
template<Mutability T>
struct ConstMutability: std::true_type{};
template<>
struct ConstMutability<Mutability::Non_Const>: std::false_type{};
What I've done so far is map Mutability::Const
to std::true_type
, and Mutability::Non_Const
to std::false_type
Now I can combine with std::enable_if
to enforce that the following are valid combinations:
U
isConst
,V
isConst
U
Non_Const
,V
isConst
U
isNon_Const
andV
isNon_Const
Full Code:
template <typename T, Mutability U>
class Obj
{
public:
template <Mutability V, typename std::enable_if<
std::is_same<ConstMutability<U>, ConstMutability<V>>::value ||
(ConstMutability<V>::value && !ConstMutability<U>::value)
>::type* = nullptr>
class Ref
{
public:
Ref() {std::cout << "Successfully created a Ref object" << std::endl;}
friend class Obj;
};
Obj() {}
};
And you can test is thusly:
int main()
{
Obj<int, Mutability::Const>::Ref<Mutability::Const> test1; //pass
//Obj<int, Mutability::Const>::Ref<Mutability::Non_Const> test2; // fail
Obj<int, Mutability::Non_Const>::Ref<Mutability::Const> test3; // pass
Obj<int, Mutability::Non_Const>::Ref<Mutability::Non_Const> test4; // pass
}
Live Demo
回答2:
What you're trying to do is to partially explicitly specialize a member class template. That is just not valid C++, same idea as basically trying to partially specialize a member. It's unfortunate that VC crashes trying to compile this, and gcc doesn't really give a helpful error, but clang happens to:
main.cpp:21:31: error: cannot specialize a dependent template
class Obj<T, Const>::template Ref<Non_Const>
^
You can however explicitly specialize the whole way down:
template <>
template <>
class Obj<int, Const>::Ref<Non_Const>
{
private:
Ref() {}
};
And then your only compile error is that Ref()
is private
.
来源:https://stackoverflow.com/questions/30852246/partial-specialization-of-nested-template-types-produces-internal-error-in-vc