从幻数到 int 或 long 的重载解析(在 range-v3 中)
overload resolution from magic number to int or long (in range-v3)
在 range-v3 中,view_facade
class 具有 begin()
功能。
template<typename D = Derived, CONCEPT_REQUIRES_(Same<D, Derived>())>
detail::facade_iterator_t<D> begin()
{
return {range_access::begin_cursor(derived(), 42)};
}
而range_access::begin_cursor()
是这样实现的,
template<typename Rng>
static RANGES_CXX14_CONSTEXPR auto begin_cursor(Rng & rng, long) // --1
RANGES_DECLTYPE_AUTO_RETURN
(
rng.begin_cursor()
)
template<typename Rng>
static RANGES_CXX14_CONSTEXPR auto begin_cursor(Rng & rng, int) // --2
RANGES_DECLTYPE_AUTO_RETURN
(
static_cast<Rng const &>(rng).begin_cursor()
)
在我的 VS 中,似乎总是调用第二个函数。
我想知道幻数 (42) 何时转换为 long
以调用第一个函数。
给编译器一个合适的文字?
return {range_access::begin_cursor(derived(), 42l)};
(是"forty-two el",不是数字421。:))
鉴于 RANGES_DECLTYPE_AUTO_RETURN
is defined as:
#define RANGES_DECLTYPE_AUTO_RETURN(...) \
-> decltype(__VA_ARGS__) \
{ return (__VA_ARGS__); } \
/**/
那么你的两个重载(在宏扩展之后)变成:
template<typename Rng>
static auto begin_cursor(Rng & rng, long) // --1
-> decltype(rng.begin_cursor())
{
return rng.begin_cursor()
}
template<typename Rng>
static auto begin_cursor(Rng & rng, int) // --2
-> decltype(static_cast<Rng const &>(rng).begin_cursor())
{
return static_cast<Rng const &>(rng).begin_cursor();
}
使用 int
参数调用 begin_cursor
时,重载解析会找到一个完全匹配的第二个重载。它优于 long
,因为它需要参数表达式的转换。但是,如果 static_cast<Rng const &>(rng).begin_cursor()
无效,即成员函数 begin_cursor()
不是 const 限定的, decltype
说明符内的表达式将触发替换失败,因此,编译器将继续搜索另一个重载,回退到第一个函数。
RANGES_DECLTYPE_AUTO_RETURN
(
static_cast<Rng const &>(rng).begin_cursor()
)
扩展为
-> decltype(static_cast<Rng const &>(rng).begin_cursor())
{ return static_cast<Rng const &>(rng).begin_cursor(); }
这两个 begin_cursor
有不同的 ->decltype
return 值,这给了你 SFINAE。考虑两种过载。如果 const
版本由于 decltype
表达式在直接上下文中格式错误而导致 SFINAE 失败,则将其删除,并选择 long
版本。
如果不是,42
更愿意转换为 int
而不是 long
。所以选择 const
版本。
在 range-v3 中,view_facade
class 具有 begin()
功能。
template<typename D = Derived, CONCEPT_REQUIRES_(Same<D, Derived>())>
detail::facade_iterator_t<D> begin()
{
return {range_access::begin_cursor(derived(), 42)};
}
而range_access::begin_cursor()
是这样实现的,
template<typename Rng>
static RANGES_CXX14_CONSTEXPR auto begin_cursor(Rng & rng, long) // --1
RANGES_DECLTYPE_AUTO_RETURN
(
rng.begin_cursor()
)
template<typename Rng>
static RANGES_CXX14_CONSTEXPR auto begin_cursor(Rng & rng, int) // --2
RANGES_DECLTYPE_AUTO_RETURN
(
static_cast<Rng const &>(rng).begin_cursor()
)
在我的 VS 中,似乎总是调用第二个函数。
我想知道幻数 (42) 何时转换为 long
以调用第一个函数。
给编译器一个合适的文字?
return {range_access::begin_cursor(derived(), 42l)};
(是"forty-two el",不是数字421。:))
鉴于 RANGES_DECLTYPE_AUTO_RETURN
is defined as:
#define RANGES_DECLTYPE_AUTO_RETURN(...) \
-> decltype(__VA_ARGS__) \
{ return (__VA_ARGS__); } \
/**/
那么你的两个重载(在宏扩展之后)变成:
template<typename Rng>
static auto begin_cursor(Rng & rng, long) // --1
-> decltype(rng.begin_cursor())
{
return rng.begin_cursor()
}
template<typename Rng>
static auto begin_cursor(Rng & rng, int) // --2
-> decltype(static_cast<Rng const &>(rng).begin_cursor())
{
return static_cast<Rng const &>(rng).begin_cursor();
}
使用 int
参数调用 begin_cursor
时,重载解析会找到一个完全匹配的第二个重载。它优于 long
,因为它需要参数表达式的转换。但是,如果 static_cast<Rng const &>(rng).begin_cursor()
无效,即成员函数 begin_cursor()
不是 const 限定的, decltype
说明符内的表达式将触发替换失败,因此,编译器将继续搜索另一个重载,回退到第一个函数。
RANGES_DECLTYPE_AUTO_RETURN
(
static_cast<Rng const &>(rng).begin_cursor()
)
扩展为
-> decltype(static_cast<Rng const &>(rng).begin_cursor())
{ return static_cast<Rng const &>(rng).begin_cursor(); }
这两个 begin_cursor
有不同的 ->decltype
return 值,这给了你 SFINAE。考虑两种过载。如果 const
版本由于 decltype
表达式在直接上下文中格式错误而导致 SFINAE 失败,则将其删除,并选择 long
版本。
如果不是,42
更愿意转换为 int
而不是 long
。所以选择 const
版本。