Search code examples
c++templatesc++17modularity

C++ Dummy template parameter inducing error while using a typedef in derived template class


I'm trying to fix a weird error I'm having with this code. This is a minimal example that can replicate the error:

test11.cpp:

namespace detail{
   template <auto UInt>
   class Test{
      public:
         typedef decltype(UInt) value_type;

         Test (const value_type& x = 0);

      protected:
         value_type n;
   };

   template<auto UInt>
   Test<UInt>::Test (const value_type& x) : n(x){}

   // Here, void would be substitute with some enable_if stuff
   template <auto UInt, typename = void>
   class TestChild : public Test<UInt>{
      public:
         typedef typename Test<UInt>::value_type value_type;

         TestChild (const value_type& x = 0);
         value_type foo() const;
   };

   template<auto UInt>
   TestChild<UInt>::TestChild (const value_type& x) : Test<UInt>(x){}

   template<auto UInt>
   value_type TestChild<UInt>::foo(){
      value_type ret=42;
      return ret;
   }
}

int main(){}

I compiled it using GCC 7.2.0 and Clang 5.0.0 in Ubuntu 16.04 LTS. You can see these two links for demos:

The error message in gcc is this one:

test11.cpp:27:38: error: ‘value_type’ does not name a type
    TestChild<UInt>::TestChild (const value_type& x) : Test<UInt>(x){}
                                      ^~~~~~~~~~
test11.cpp:27:51: error: invalid use of incomplete type ‘class detail::TestChild<UInt>’
    TestChild<UInt>::TestChild (const value_type& x) : Test<UInt>(x){}
                                                   ^
test11.cpp:18:10: note: declaration of ‘class detail::TestChild<UInt>’
    class TestChild : public Test<UInt>{
          ^~~~~~~~~
test11.cpp:30:4: error: ‘value_type’ does not name a type
    value_type TestChild<UInt>::foo(){
    ^~~~~~~~~~

Also, the thing which is really strange for me is that, if I omit the dummy parameter in the template for the class TestChild (I mean, template<auto UInt> instead of template<auto UInt, typename = void>), I still get a (shorter) error that looks like this:

test11.cpp:30:4: error: ‘value_type’ does not name a type
    value_type TestChild<UInt>::foo(){
    ^~~~~~~~~~

You can check the demo for GCC 7.2.0 here.

It looks like the main error is that, if I define the functions outside the class, the typedef typename Test<UInt>::value_type value_type; isn't really detected (I don't know if I explained myself well).

This will make sense for me if it also happens when I define the functions inside the class, but that's not the case, because everything compiles fine in the last case (you can see it in the demo if you want to).

In conclusion, my main question is that I want to compile this program, but taking in mind that I must separate declaration from definition. So the last demo I showed (where everything is defined inside the class) is what I want to achieve, but modularizing.

I hope someone can help me and explain what is going on with this code.


Solution

  • You should make three (at least) changes.

    First: add the second (default) template value for TestChild.

    So

    //................vvvvvvvvvvvv
    template<auto UInt, typename V>
    TestChild<UInt, V>::TestChild (const value_type& x) : Test<UInt>(x){}
    //............^^^
    

    and the same for the foo() method

    Second: remember that foo() is const

    So

    template <auto UInt, typename V>
    value_type TestChild<UInt, V>::foo() const {
       value_type ret=42;  // ...........^^^^^
       return ret;
    }
    

    Third: for the return type for foo() explicit the class

    So typename TestChild<UInt, V>::value_type instead of value_type

    template <auto UInt, typename V>
    typename TestChild<UInt, V>::value_type TestChild<UInt, V>::foo() const {
       value_type ret=42;
       return ret;
    }
    

    Or, if you prefer, you can use the auto ... -> return type syntax

    template<auto UInt, typename V>
    auto TestChild<UInt, V>::foo() const -> value_type {
       value_type ret=42;
       return ret;
    }
    

    or simply auto (but also in the declaration of the method)

    auto foo() const;
    
    // ...
    
    template<auto UInt, typename V>
    auto TestChild<UInt, V>::foo() const {
       value_type ret=42;
       return ret;
    }