c++templatesnamespacesc++20formatter

Why can't I specialize std::formatter inside of my namespace?


I want to specialize std::formatter inside my namespace. However, this does not work as I want to (variant 1). It works outside the namespace (variant 3) as well as with a workaround inside the namespace (variant 2).

#include <format>
#include <iostream>
#include <string>
#include <type_traits>

class Class {};

#define VARIANT 1

namespace Namespace {

#if VARIANT == 1
template <>
struct std::formatter<Class> : std::formatter<std::string> {
    auto format(const Class& Class, auto& context) const {
        return std::formatter<std::string>::format("CLASS", context);
    }
};
#endif

#if VARIANT == 2
template <typename T>
    requires std::same_as<T, Class>
struct std::formatter<T> : std::formatter<std::string> {
    auto format(const Class& Class, auto& context) const {
        return std::formatter<std::string>::format("CLASS", context);
    }
};
#endif

}  // namespace Namespace

#if VARIANT == 3
template <>
struct std::formatter<Class> : std::formatter<std::string> {
    auto format(const Class& Class, auto& context) const {
        return std::formatter<std::string>::format("CLASS", context);
    }
};
#endif

int main() { std::cout << std::format("{}", Class()) << std::endl; }

This is on x64 msvc v19.40 VS17.10. gcc compiles only variant 3.

So my questions are, what are standard-conforming, portable ways of specialing std::formatter inside namespaces (or generally)?


Solution

  • So my questions are, what are standard-conforming, portable ways of specialing std::formatter inside namespaces (or generally)?

    There aren't any.

    You can only specialize a class template in places in which you can define the primary template. That is, you either have to do this (at global scope):

    template <>
    struct std::formatter<MyClass> { ... };
    

    Or you do this (within std):

    namespace std {
        template <>
        struct formatter<MyClass> { ... };
    }
    

    But you cannot do this or anything like it:

    namespace N {
        template <>
        struct ::std::formatter<MyClass> { ... };
    }
    

    More generally, given something like:

    namespace N::M {
        template <class T> struct C;
    }
    

    All of these are fine:

                     template <> struct N::M::C<int> { };   // ok
    namespace N    { template <> struct M::C<double> { }; } // ok
    namespace N::M { template <> struct C<float> { }; }     // ok
    
    N::M::C<int> a;     // ok
    N::M::C<double> b;  // ok
    N::M::C<float> c;   // ok
    

    But not anywhere else.