1

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;
}
Madison Brown
  • 331
  • 3
  • 10
  • C1001 basically means the compiler has crashed so on the one hand this is a compiler bug (VS2013 does the same thing), however it fails to compile with gcc as well : http://ideone.com/BhT1bB – Jonathan Potter Jun 15 '15 at 18:25
  • That's awfully - not circular, but somehow a circular declaration. –  Jun 15 '15 at 18:28

2 Answers2

1

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.

Barry
  • 286,269
  • 29
  • 621
  • 977
0

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 is Const, V is Const
  • U Non_Const, V is Const
  • U is Non_Const and V is Non_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

AndyG
  • 39,700
  • 8
  • 109
  • 143