4

I have a variant class which uses some function template specialization for getting and setting different types which compiled and worked fine with Visual Studio 2010. However this code was located in a common solution which also needs to compile on redhat, ubuntu, etc.

I received an error along the lines of explicit specialization in non-namespace scope. I figured the easy fix was to simply define my specializations outside the class with scope qualifier for the class in the same namespace.

However now I'm getting errors that the specialization occurs after the instantiation as other methods of the class for converting from various types are using this template within the class.

So what is the correct way to do something like this:

namespace Example
{
    class CSomeVariant
    {
    public:
        bool toString(std::string& out)
        {
            return get(out);
        }
    private:
        template <typename T>
        bool get(T& val)
        {
            try {
                val = boost::any_cast<T>(m_stored);
            }
            catch (...) {
                return false;
            }
            return true;
        }

        boost::any m_stored;
    };

    template<>
    bool CSomeVariant::get(std::string& val)
    {
        try {
            if (m_stored.type() != typeid(std::string))
               val = convertToString();
            else
               val = boost::any_cast<std::string>(m_stored);
        }
        catch(...) {
            return false;
        }
        return true;
    }
}

Note: This is not the actual code but I believe it shows the problem.

AJG85
  • 15,849
  • 13
  • 42
  • 50

1 Answers1

3

The problem is that you're using the get() function in the class definition and then specializing it afterwards, which is not allowed. From 14.7.3 paragraph 6

If a template, a member template or the member of a class template is explicitly specialized then that specialization shall be declared before the first use of that specialization that would cause an implicit instantiation to take place, in every translation unit in which such a use occurs; no diagnostic is required. If the program does not provide a definition for an explicit specialization and either the specialization is used in a way that would cause an implicit instantiation to take place or the member is a virtual member function, the program is ill-formed, no diagnostic required.

One solution is to reorder the class definition so that the specialization is declared before any uses. In this case, I was able to move the inline use of the function till after the specialization.

#include <string>
#include <boost/any.hpp>
namespace Example
{
    class CSomeVariant
    {
    public:
        bool toString(std::string& out);

    private:
       template <typename T>
       bool get(T& val)
       {
           try {
              val = boost::any_cast<T>(m_stored);
           }
           catch (...) {
               return false;
           }
           return true;
       }

       boost::any m_stored;
   };

   template<>
   bool CSomeVariant::get(std::string& val)
   {
       try {
           if (m_stored.type() != typeid(std::string))
              val = convertToString();
           else
              val = boost::any_cast<std::string>(m_stored);
       }
       catch(...) {
           return false;
       }
       return true;
   }


   inline bool CSomeVariant::toString(std::string& out)
   {
        return get(out);
   }
}
Dave S
  • 20,507
  • 3
  • 48
  • 68
  • You beat me by 1 minute! I had just read about this today in my newly arrived copy of Advanced C++ MetaProgramming. – TemplateRex May 03 '12 at 20:29
  • There are a ton of toThis or toThat methods but this would work. I'm going to go with ildjarn's suggestion as I like doing away with the specializations entirely. – AJG85 May 03 '12 at 20:32
  • 3
    @AJG85 As a general rule, I try to avoid specializing functions, and use overloading whenever I can. The rules are easier to follow. – Dave S May 03 '12 at 20:39