Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to define constructor of fully-specialized class within another class template

I have a class template that contains another class template, and the inner template has an explicit specialization:

template <typename Outer>
struct ContainingClass {
  template <typename T>
  struct Rule {
    Rule(T value);

    // ... other members ...
  };

  template <>
  struct Rule<void> {
    Rule();

    // ... different members than the non-void Rule<T> ...
  };
};

I've defined the constructors for both the generic and specialized Rule:

template <typename Outer>
template <typename T>
ContainingClass<Outer>::Rule<T>::Rule(T value) { }

template <typename Outer>
ContainingClass<Outer>::Rule<void>::Rule() { }

But Clang doesn't like the specialized class's constructor:

error: nested name specifier 'ContainingClass<Outer>::Rule<void>::' for declaration does not refer into a class, class template or class template partial specialization
ContainingClass<Outer>::Rule<void>::Rule() { }
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^

I'm puzzled by this, because it "refers into" ContainingClass<Outer>, which is a class (an instance of the ContainingClass class template). I suspect I need to change something in the syntax for this, but it's not clear what. How can I define this constructor?

(It works if I remove ContainingClass and put Rule at namespace scope, but I need to have other things in Rule that depend on the Outer type. I could give Rule its own Outer template parameter, but that'll make things more awkward for the code that uses this class, so I'd like to avoid it if possible. And I know I can define the constructor inline within the Rule class body, but I'd like to understand why the separate definition isn't working.)

In case it matters, I'm using both Clang 8.0 in Ubuntu 19.04, and Apple's "clang-1001.0.46.4" on a Mac. (I've also tried Ubuntu's GCC 8.3, but that fails in a different place due to GCC bug #85282 — "explicit specialization in non-namespace scope" on the definition of struct Rule<void> itself.)

Edited to clarify: My error isn't about having the template <> struct Rule<void> specialization within ContainingClass. That's the subject of a C++14 limitation (defect CWG 727), which is worked around by adding a dummy template parameter so the template is only partially- instead of fully-specialized. I believe that limitation has been lifted in C++17, and the specialization of the Rule class itself works fine in Clang (though GCC has the bug). So I think the dummy parameter workaround is not the right solution here — but please tell me if I'm mistaken and there are still limitations on this in C++17.

like image 297
Wyzard Avatar asked Jul 04 '19 17:07

Wyzard


1 Answers

You can not declare at namespace scope a member of a specialization of template member of a template.

To avoid this limitation, you can use a work around that used to be necessary for c++14 and earlier which consists in using a partial specialization instead of a full specialization:

template <typename Outer>
struct ContainingClass {
  template <typename T,class=void>
  struct Rule {
    Rule(T value);

    // ... other members ...
  };

  template <class U>
  struct Rule<void,U> {
    Rule();

    // ... different members than the non-void Rule<T> ...
  };
};

template <typename Outer>
template <typename T, typename U>
ContainingClass<Outer>::Rule<T,U>::Rule(T value) { }

template <typename Outer>
template <typename U>
ContainingClass<Outer>::Rule<void,U>::Rule() { }

In C++17 it is still impossible to declare (a member of) a specialization member of a class template at namespace scope, see [temp.expl.spec]/17. The same paragraph exists in the C++14 standard.

What changed with C++17 is that we can declare a specialization of the member inside the enclosing class template definition:

  • C++14/[templ.expl.spec]/2

An explicit specialization shall be declared in a namespace enclosing the specialized template.[...]

  • C++17/[templ.expl.spec]/2

An explicit specialization may be declared in any scope in which the corresponding primary template may be defined.[...]

like image 95
Oliv Avatar answered Oct 23 '22 05:10

Oliv