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.
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:
An explicit specialization shall be declared in a namespace enclosing the specialized template.[...]
An explicit specialization may be declared in any scope in which the corresponding primary template may be defined.[...]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With