views:

120

answers:

2

I'm trying to port some code from VC9 to G++, however Ive run into a problem with template specialisations apparently not being allowed for class members.

The following code is an example of these errors for the getValue specialisations of the class methods. In all cases the error is "error: explicit specialization in non-namespace scope class ..."

template<typename T> T getValue(const_iterator key)const
{
    try{return boost::lexical_cast<T>(key->second);}
    catch(boost::bad_lexical_cast &e)
    {
        throw TypeParseError<T>(name, key->first, e.what());
    }
}
template<typename T> T getValue(const std::string &key)const
{
    iterator i = find(key);
    if(i == end())throw KeyNotFound(name,key);
    else return getValue(i);
}
template<> std::string getValue<std::string>(const_iterator key)const
{
    return key->second;
}
template<> std::string getValue<std::string>(const std::string &key)const
{
    const_iterator i = find(key);
    if(i == end())throw KeyNotFound(name,key);
    else return i->second;
}

Is it just the exact syntax is not supported, and that a minor change will make it work, or will I need to change the code to avoid specialisations like this? If the latter which is the best way to do so in general?

+7  A: 

You haven't shown the class definition enclosing these function-declarations. But i assume it's some class where these templates are declared in. You have to define the specializations outside:

struct SomeClass {
   template<typename T> T getValue(const_iterator key)const
    {
        try{return boost::lexical_cast<T>(key->second);}
        catch(boost::bad_lexical_cast &e)
        {
            throw TypeParseError<T>(name, key->first, e.what());
        }
    }
    template<typename T> T getValue(const std::string &key)const
    {
        iterator i = find(key);
        if(i == end())throw KeyNotFound(name,key);
        else return getValue(i);
    }
};

template<> inline std::string SomeClass::getValue<std::string>(const_iterator key)const {
    return key->second;
}

template<> inline std::string SomeClass::getValue<std::string>(const std::string &key)const {
    const_iterator i = find(key);
    if(i == end())throw KeyNotFound(name,key);
    else return i->second;
}

Remember that since you have defined them outside, they are not inline implicitly, so you either have to make them inline explicitly, or move them into a cpp file (not a header), and forward-declare the specializations in the header like this:

template<> inline std::string SomeClass::getValue<std::string>(const_iterator key)const;
template<> inline std::string SomeClass::getValue<std::string>(const std::string &key)const;

If you omit the forward-declaration, the compiler has no way to know whether to instantiate the functions or to use the explicit specializations. The forward declaration tells it.

Johannes Schaub - litb
+1 for the answer, also see mine for the excerpt of the standard.
Gregory Pakosz
+3  A: 

MSVC allows explicit specialization at class scope whereas the standard does not.

From what I can see, the code you're giving comes from a class definition. Specialize outside the scope of the class.

template<> inline std::string Foo::getValue<std::string>(const_iterator key)const {
    return key->second;
}

template<> inline std::string Foo::getValue<std::string>(const std::string &key)const {
    const_iterator i = find(key);
    if(i == end())throw KeyNotFound(name,key);
    else return i->second;
}

See:

14.7.3.2:

An explicit specialization shall be declared in the namespace of which the template is a member, or, for member templates, in the namespace of which the enclosing class or enclosing class template is a member. An explicit specialization of a member function, member class or static data member of a class template shall be declared in the namespace of which the class template is a member.

Gregory Pakosz