is_error_code_enum<>枚举必须只在全局命名空间中定义?

时间:2015-01-04 10:38:41

标签: c++ c++11 overloading

我正在尝试创建自己的基于枚举的错误类别,并希望在某些命名空间中定义带有错误代码的枚举。令我惊讶的是,这阻止了我的枚举值自动转换为std::error_code(如果在全局命名空间中定义了枚举,则此类转换有效)。

#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 !!
}

我在上面的代码中遗漏了某些内容(可能与重载决策规则有关),以使其有效吗?或者std::is_error_code_enum<>的枚举只能在全局命名空间中定义吗?

EDIT。我的编译器(MSVC2013)没有抱怨它,但它似乎是std :: is_error_code_enum&lt;&gt;的特化。必须在std命名空间内完成。我还在name()方法上添加了noexcept关键字,以使代码更符合C ++ 11(MSVC2013不会理解noexcept,但MSVC2015会这样做。)

EDIT2。根据C ++ 11 14.7.3.2 [temp.expl.spec]:

  

应在包含专用模板的命名空间中声明显式特化。

因此没有必要对std :: is_error_code_enum&lt;&gt;进行专门化。在std命名空间内。 MSVC正确编译它,但是GCC抱怨这实际上是GCC中的一个错误,因为GCC的行为是旧的C ++ 03规则,这些规则更具限制性。

2 个答案:

答案 0 :(得分:2)

error_code中的构造函数模板确实被认为是在重载决策中 - 您正确地专注于is_error_code。问题是error_code构造函数模板定义中此行中的ADL:

*this = make_error_code(__e);

ADL不考虑全局命名空间,因为X仅在NS中定义,而不是全局命名空间。 [basic.lookup.argdep] /(2.3):

  

如果T是枚举类型,则其关联的命名空间是   其声明的最内部封闭命名空间。 [..]

使用声明不会改变这一点。 [basic.lookup.argdep] / 2:

  

命名空间和类的集合完全由类型决定   函数参数(以及任何模板模板的命名空间)   参数)。   用于指定类型的Typedef名称和 using-declaration

要解决此问题,请将您的make_error_code添加到NS:

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

Demo

答案 1 :(得分:1)

我认为问题是大多数设置代码都需要在命名空间内。这段代码在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();
}

不幸的是,我仍然无法理解system_error,所以我无法解释为什么,例如,使用枚举类而不仅仅是简单的枚举给我一个未指定的运行时错误。