2

I've implemented an enum <-> string conversion library using Boost.Preprocessor. In a header file it declares a few templates, e.g. template<typename T> inline const std::string to_string(const T& v) = delete;, then any time the macro is used it defines an enum class and an explicit (full) template specialization, e.g. enum class MyEnum {...}; template<> inline const std::string to_string<MyEnum>(const MyEnum& v) {...}; The problem is that this only works as long as the macro is used in the same namespace as the primary template declarations.

From this answer, in c++11 it is possible to define a full specialization outside the namespace of the primary template declarations as long as it's an enclosing namespace. However, this is a problem since the idea is to be able to use the library in any arbitrary scope.

An explicit specialization shall be declared in a namespace enclosing the specialized template. An explicit specialization whose declarator-id is not qualified shall be declared in the nearest enclosing namespace of the template, or, if the namespace is inline (7.3.1), any namespace from its enclosing namespace set.

I thought maybe I could potentially make the declarations part of the macro, in an inline namespace, but I haven't figured out a way to ensure that they are only included once per namespace if the macro is used multiple times - otherwise I get an "ambiguous template specialization" error.

Each member of an inline namespace can be partially specialized , explicitly instantiated or explicitly specialized as if it were a member of the enclosing namespace.

Which brings me to my question, of whether I can bring in the template names (defined in namespace whose parent is the global namespace) with using-declarations, and then define explicit specializations. Something like this:

namespace a {
    template<typename T>
    inline const std::string some_func(T) = delete;
}

inline namespace b {
    using a::some_func;

    template<> inline const std::string some_func(int v)  { return "some_func(int)"; }
    template<> inline const std::string some_func(char v) { return "some_func(char)"; }
}

If I try to compile that with g++ I get (it compiles and works as expected if I use -fpermissive):

error: explicit specialization of ‘template<class T> const string a::some_func(T)’ outside its namespace must use a nested-name-specifier [-fpermissive]
   13 |     template<> inline const std::string some_func(int v)  { return "some_func(int)"; }

With clang:

error: no function template matches function template specialization 'some_func'
    template<> inline const std::string some_func(int v)  { return "some_func(int)"; }
                                        ^

If I use its nested-name-specifier template<> inline const std::string a::some_func(int v) {...}, then with g++ I get:

error: declaration of ‘const string some_func(int)’ not in a namespace surrounding ‘a’
   13 |     template<> inline const std::string a::some_func(int v)  { return "some_func(int)"; }
      |                                                           ^
error: specialization of ‘template<class T> const string a::some_func(T)’ in different namespace [-fpermissive]
note:   from definition of ‘template<class T> const string a::some_func(T)’
    7 |     inline const std::string some_func(T) = delete;
      |                              ^~~~~~~~~

With clang:

error: cannot define or redeclare 'some_func' here because namespace 'b' does not enclose namespace 'a'
    template<> inline const std::string a::some_func(int v)  { return "some_func(int)"; }
                                        ~~~^

I don't quite understand why doing this is not permissive, from using-declarations:

Names introduced into a namespace scope by a using-declaration can be used just like any other names, including qualified lookup from other scopes.

From the notes here I see that (below), but I'm bringing in the primary template name, not a specialization.

A using-declaration cannot name a member template specialization (template-id is not permitted by the grammar):

struct B { template<class T> void f(); };
struct D : B {
      using B::f;      // OK: names a template
//    using B::f<int>; // Error: names a template specialization
      void g() { f<int>(); }
};

I'm not too sure I fully understand template-id, but I don't think that's the issue here since the example actually brings in a template name.

Sorry for the long question, and if anyone knows of a different solution to my problem that would also be very welcome :)

Ramon
  • 1,169
  • 11
  • 25

0 Answers0