Is this explicit template function specialization of a member template of a member template of a class template valid?

484 views Asked by At

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!

1

There are 1 answers

2
WhozCraig On

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]