is_error_code_enum<> 枚举只能在全局命名空间中定义?
is_error_code_enum<> enumeration must be defined in global namespace only?
我正在尝试创建我自己的基于枚举的错误类别,并希望我的带有错误代码的枚举在某个命名空间内定义。令我惊讶的是,这阻止了我的枚举值自动转换为 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<>
的枚举只能在全局命名空间内定义??
编辑。我的编译器(MSVC2013)没有抱怨它,但似乎 std::is_error_code_enum<> 的专业化必须在 std 命名空间内完成。此外,我在 name() 方法上添加了 noexcept 关键字,使代码更加符合 C++11(MSVC2013 不理解 noexcept,但 MSVC2015 会)。
编辑2。根据 C++11 14.7.3.2 [temp.expl.spec]:
An explicit specialization shall be declared in a namespace enclosing the specialized template.
因此没有必要将 std::is_error_code_enum<> 的特化放在 std 命名空间中。 MSVC 正确编译它,但 GCC 抱怨这实际上是 GCC 中的一个错误,因为 GCC 遵循更严格的旧 C++03 规则。
我认为问题在于大部分设置代码需要位于名称空间内。此代码在 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,所以我无法解释为什么,例如,使用枚举 class 而不是简单的枚举会给我一个未指定的运行时错误。
来自 error_code
的构造函数模板确实被考虑在重载决策中 - 您正确地特化了 is_error_code
。问题是在 error_code
s 构造函数模板定义的这一行中的 ADL:
*this = make_error_code(__e);
ADL 不考虑全局命名空间,因为X
仅在NS
中定义,而不是全局命名空间。 [basic.lookup.argdep]/(2.3):
If T
is an enumeration type, its associated namespace is the
innermost enclosing namespace of its declaration. [..]
using 声明不会改变这一点。 [basic.lookup.argdep]/2:
The sets of namespaces and classes is determined entirely by the types
of the function arguments (and the namespace of any template template
argument).
Typedef names and using-declarations used to specify the types do not contribute to this set.
要解决此问题,请将您的 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.
我正在尝试创建我自己的基于枚举的错误类别,并希望我的带有错误代码的枚举在某个命名空间内定义。令我惊讶的是,这阻止了我的枚举值自动转换为 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<>
的枚举只能在全局命名空间内定义??
编辑。我的编译器(MSVC2013)没有抱怨它,但似乎 std::is_error_code_enum<> 的专业化必须在 std 命名空间内完成。此外,我在 name() 方法上添加了 noexcept 关键字,使代码更加符合 C++11(MSVC2013 不理解 noexcept,但 MSVC2015 会)。
编辑2。根据 C++11 14.7.3.2 [temp.expl.spec]:
An explicit specialization shall be declared in a namespace enclosing the specialized template.
因此没有必要将 std::is_error_code_enum<> 的特化放在 std 命名空间中。 MSVC 正确编译它,但 GCC 抱怨这实际上是 GCC 中的一个错误,因为 GCC 遵循更严格的旧 C++03 规则。
我认为问题在于大部分设置代码需要位于名称空间内。此代码在 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,所以我无法解释为什么,例如,使用枚举 class 而不是简单的枚举会给我一个未指定的运行时错误。
来自 error_code
的构造函数模板确实被考虑在重载决策中 - 您正确地特化了 is_error_code
。问题是在 error_code
s 构造函数模板定义的这一行中的 ADL:
*this = make_error_code(__e);
ADL 不考虑全局命名空间,因为X
仅在NS
中定义,而不是全局命名空间。 [basic.lookup.argdep]/(2.3):
If
T
is an enumeration type, its associated namespace is the innermost enclosing namespace of its declaration. [..]
using 声明不会改变这一点。 [basic.lookup.argdep]/2:
The sets of namespaces and classes is determined entirely by the types of the function arguments (and the namespace of any template template argument). Typedef names and using-declarations used to specify the types do not contribute to this set.
要解决此问题,请将您的 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.