20

Here is some code which does not compile.

namespace ns
{
    class foo
    {
        template <typename T> int bar (T *);
    };
}

template <typename T>
int ns :: foo :: bar (T*) // this is OK
{
    return 0;
}

template <>
int ns :: foo :: bar <int> (int *) // this is an error
{
    return 1;
}

The error is: "specialisation of ‘template int ns::foo::bar(T*)’ in different namespace [-fpermissive] from definition of ‘template int ns::foo::bar(T*)"

Here is a version which does compile:

namespace ns
{
    class foo
    {
        template <typename T> int bar (T *);
    };
}

template <typename T>
int ns :: foo :: bar (T*)
{
    return 0;
}

namespace ns
{
    template <>
    int foo :: bar <int> (int *)
    {
        return 1;
    }
}

Why does the second definition have to be in a namespace ns {} block when the first one is quite happily defined with a qualified name? Is it just an oversight in the language design or is there a reason for this?

spraff
  • 32,570
  • 22
  • 121
  • 229
  • Now that I look at it, C++11 § 14.7.3/2 is for the decl, not the def. But I think 14.7.3/8 still applies. The wording at the end of 14.7.3/7 is *awesome*: "When writing a specialization, be careful about its location; or to make it compile will be such a trial as to kindle its self-immolation." – WhozCraig Aug 18 '13 at 02:14
  • [possible duplicate?](http://stackoverflow.com/questions/3052579/explicit-specialization-in-non-namespace-scope) – yngccc Aug 18 '13 at 02:21
  • Heh. according to Georg it *is* in fact 14.7.3/2. Teach *me* to second guess myself. – WhozCraig Aug 18 '13 at 02:24
  • another similar question: http://stackoverflow.com/questions/3072248/why-arent-template-specializations-allowed-to-be-in-different-namespaces – a.lasram Aug 18 '13 at 02:24
  • That relates to which namespace a function lives in, but this question is about why the namespace must be expressed in a particular (equivalent) way. – spraff Aug 18 '13 at 02:27
  • @spraff: The problem is not where the element is *defined*, but rather where it is *declared*. You can *define* it as you want, but you need to provide a declaration in the appropriate namespace. – David Rodríguez - dribeas Aug 18 '13 at 02:52

1 Answers1

14

The problem here is not the definition, but the declaration. You cannot inject a declaration in a namespace from a different namespace, so the specialization must be declared in the appropriate namespace before it can be defined in any enclosing namespace.

The definition of the base template can be done in the outer namespace because it has already been declared, so the code in the outer namespace provides a definition but does not inject any declaration into the namespace.

Try:

namespace ns {
    class foo
    {
        template <typename T> int bar (T *);
    };
    template <>
    int foo::bar<int>(int*); // declaration
}
template <typename T>
int ns :: foo :: bar (T*) {
    return 0;
}
template <>
int ns :: foo :: bar <int> (int *) {
    return 1;
}
David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
  • +1 This makes *perfect* sense. I think this was the fundamental behind 14.7.3/2. Thanks for the clarity, David. – WhozCraig Aug 18 '13 at 03:33
  • Thanks, but can you please clarify: how is line 5 in my original code `template int bar (T *);` *not* a declaration? – spraff Aug 18 '13 at 08:46
  • @spraff: It *is* a declaration, but not a declaration than a specialization exist. – Matthieu M. Aug 18 '13 at 09:40
  • Perhaps this is a silly question, but why must a declaration that a specialization exist exist? – spraff Aug 18 '13 at 12:30
  • @spraff: A definition is a declaration, 8.3/1 requires that a declaration in which the *declarator-id* is qualified (as is your case) the it must refer to a previously declared member of the class or namespace to which the qualifier refers. (Informally I usually say that a *definition* outside the namespaace is not a *declaration*, but that is really not the case, it is just that it requires of a previous declaration within the namespace) – David Rodríguez - dribeas Aug 18 '13 at 20:54