Is_error_code_enum <> enum should only be defined in the global namespace?

I am trying to create my own enum based error category and want my enum with error codes to be defined inside some namespace. Much to my surprise, this prevents the automatic conversion of my enum values ​​to std::error_code

(this conversion works if the enumeration is defined in the global namespace).

#include <system_error>

namespace NS { enum class X {a,b,c}; }
using NS::X;

class X_category : public std::error_category
{
public:
    virtual const char *name() const noexcept override { return "X"; }
    virtual std::string message(int ev) const override { return ""; }
};

inline const std::error_category& X_category()
{
    static class X_category c; return c;
}

template<> struct std::is_error_code_enum<X> : public std::true_type{};

inline std::error_code make_error_code(X result)
{
    return std::error_code(static_cast<int>(result), X_category());
}

int main()
{
    std::error_code e = X::a; // does not work !!
}

      

Am I missing something in my code above (possibly related to overloading permission rules) to get it to work? Or can enums for std::is_error_code_enum<>

can only be defined in the global namespace?

EDIT. My compiler (MSVC2013) doesn't complain about this, but it looks like the specialization std :: is_error_code_enum <> should be done inside the std namespace. Also, I've added a noexcept keyword on the name () method to make the code even more C ++ 11 compatible (MSVC2013 won't understand noexcept, but MSVC2015 will).

EDIT2. According to C ++ 11 14.7.3.2 [temp.expl.spec]:

Explicit specialization is declared in the namespace that encompasses the specialized pattern.

Therefore, there is no need to put the specialization std :: is_error_code_enum <> inside the std namespace. MSVC compiles it correctly, but GCC complains that it is actually a bug in GCC, since GCC follows the old C ++ 03 rules, which were more restrictive.

+3


source share


2 answers


The constructor pattern from is error_code

indeed covered in overload resolution - you have specialized correctly is_error_code

. The problem is the ADL on this line in the definition of the constructor template error_code

:

*this = make_error_code(__e);

      

ADL does not consider the global namespace because it X

is only defined in NS

, not global. [Basic.lookup.argdep] / (2.3):

If T

is an enumeration type, its associated namespace is the innermost namespace of its declaration. [..]

The usage declaration does not change this. [Basic.lookup.argdep] / 2:



The sets of namespaces and classes are completely determined by the types of the function arguments (and the namespaces of any template template argument). The Typedef names and declarations used to indicate types do not contribute to this set.

To fix this, add yours make_error_code

to NS:

namespace NS {
    inline std::error_code make_error_code(X result)
    {
        return std::error_code(static_cast<int>(result), X_category());
    }
}

      

Demo .

+2


source


I believe the problem is that most of this setup code needs to be inside the namespace. This code compiles and works for me in ideone:

#include <system_error>
#include <iostream>

namespace NS {
    enum X {a,b,c};

    class X_category : public std::error_category
    {
    public:
        virtual const char *name() const noexcept override { return "X"; }
        virtual std::string message(int ev) const override { return "M"; }
    };

    inline std::error_code make_error_code(X result)
    {
        return std::error_code(static_cast<int>(result), X_category());
    }
}

namespace std {
    template<> struct is_error_code_enum<NS::X> : public true_type{};
}

int main()
{
    std::cout << NS::X::a;
    std::error_code e = NS::X::a;
    std::cout << e.value();
}

      



Unfortunately, I still can't figure out the meaning of system_error, so I can't explain why, for example, using an enum class instead of a simple enum gives me an undefined runtime error.

+1


source







All Articles