5

Does anyone know if this explicit specialization is or is not valid:

template <class>
struct L {
  template <typename T>
  struct O {
    template <typename U>
    static void Fun(U);
  };
};

template<>
template<typename T>
template<typename U>
void L<int>::O<T>::Fun(U) {}

clang trunk (12/3/2013) gives the following error:
f:...\test.cpp:36:20: error: out-of-line definition of 'Fun' from class 'O' without definition

void L<int>::O<T>::Fun(U) {}
     ~~~~~~~~~~~~~~^

1 error generated.

Any supporting references from the standard to justify your answer will be greatly appreciated!

Note: I am somewhat surprised that it is an error - I would expect the specialization to be picked for any family of template arguments with which 'Fun' is instantiated beginning with <int><?any><?any>.

Is this a clang bug or a bug in my expectations?

Thank you!

====== EDIT (I think I have an answer) ========

OK - i think i found the supporting wording - from N3797 (post-chicago 2013 working draft) - 14.7.3/16 =>

"In an explicit specialization declaration for a member of a class template or a member template that appears in namespace scope, the member template and some of its enclosing class templates may remain unspecialized, except that the declaration shall not explicitly specialize a class member template if its enclosing class templates are not explicitly specialized as well."

If i interpret this correctly, we need an explicit specialization of O if we're going to declare an explicit specialization of its member? Hence the error.

Correct?

Thanks!

M.M
  • 138,810
  • 21
  • 208
  • 365
Faisal Vali
  • 32,723
  • 8
  • 42
  • 45
  • I don't think the text you quoted is relevant; it's saying that you can't specialize `L::O`. As opposed to `L::O` which is fine. – M.M Jun 07 '18 at 01:33
  • Added language-lawyer tag as I would like to see where in the Standard it requires that you need to redefine `L::O` before this specialization is allowed – M.M Jun 07 '18 at 01:34

1 Answers1

4

I don't believe it is valid. I'm not deep enough in the language to tell you how/if you can do this without providing the top-level specialization as done below, or if there is a shortcut for skipping the replicate templates, but the error message is fairly clear: You're trying to provide an implementation of a static member of a dependent nested type without providing the specialization of the actual dependency. I.e. this works:

#include <iostream>

template <typename>
struct L
{
    template <typename T>
    struct O
    {
        template <typename U>
        static void Fun(U)
        {
            std::cout << "General: " << __PRETTY_FUNCTION__ << std::endl;
        };
    };
};

// provide specialized L
template<>
struct L<int>
{
    template <typename T>
    struct O
    {
        template <typename U>
        static void Fun(U);
    };
};

// L<int> is a specialized type, so provide the rest.
template<typename T>
template<typename U>
void L<int>::O<T>::Fun(U)
{
    std::cout << "Special: " << __PRETTY_FUNCTION__ << std::endl;
}


int main()
{
    L<int>::O<double> nobj;
    nobj.Fun(10);

    L<double>::O<int> nobj2;
    nobj2.Fun(20);

    return 0;
}

Output

Special: static void L<int>::O<double>::Fun(U) [T = double, U = int]
General: static void L<double>::O<int>::Fun(U) [T = int, U = int]
WhozCraig
  • 65,258
  • 11
  • 75
  • 141
  • I actually think you just need to provide: `template<> template struct L::O { template static void Fun(U); }; - but i'm still not sure why that's necessary? – Faisal Vali Dec 04 '13 at 01:54
  • You're correct, and with that you can use the decl for the function you specified earlier. If I get a chance (chaos @ home) I'll update this. – WhozCraig Dec 04 '13 at 02:31