std::begin-类型特征中未考虑用户定义的重载

std::begin - User-defined overloads not considered in type traits

本文关键字:用户 定义 重载 begin- 类型 特征 std      更新时间:2023-10-16

考虑以下类型特征:

template<typename T, typename = void>
struct has_begin : std::false_type {};
template<typename T>
struct has_begin<T, std::void_t<
decltype(std::begin(std::declval<std::add_lvalue_reference_t<T>>()))
>> : std::true_type {};

为什么这个特性不考虑我为std::begin定义的重载?

namespace std {
void begin(foo&) {}
}
int main() {
static_assert(has_begin<foo>::value); // Fails.
foo f;
std::begin(f); // Works.
}

实例

有趣的观察:

  1. 如果我改变这种类型特征的顺序和我的超负荷,它就起作用了
  2. 如果我在类型特征中使用ADL:

decltype(std::begin(std::add_lva... -> decltype(begin(std::add_lva...

如果自由函数beginfoo:在同一个命名空间中,它就会工作

void begin(foo) {
}

但对于std::之外的任何类都失败,具体取决于:

template<class C>
auto begin(C& c) -> decltype(c.begin());

因为ADL查找不适用于来自其他命名空间的模板。


在不更改包含顺序的情况下,我可以做些什么来支持我的类型特征中的std::begin(foo&)

否则,我必须同时支持这两个世界——为std::begin和ADL-begin((编写类型特征。。。

在我的功能中,我已经做了这样的事情(这里建议(:

auto get_begin() {
using std::begin;
return begin(object);
}

在不更改include顺序的情况下,我可以做些什么来支持我的类型特征中的std::begin(foo&(?

您没有;CCD_ 5不意味着对于任意范围直接调用。如果要访问范围类型的begin/end,则应该使用ADL和using std::begin/end。这就是这个习语在C++中的工作原理。

std命名空间中重载方法是非法的,std::begin也不例外。您可以创建std定义的模板的模板专用化(基于用户创建的类型(,但这不是使用C++习惯用法的正确方法。

在C++20中,std::ranges::begin函数是直接调用的,而将其专门化为类型的方法是通过ADL或成员begin函数。所以只要使用这个成语,每个人都会好起来的。

您可以使用一点间接方法来解决此问题。

namespace adl {
namespace std_adl {
using std::begin; using std::end;
template<class T>
auto adl_begin( T&& t )
->decltype(begin(std::forward<T>(t)) )
{ return begin(std::forward<T>(t)); }
template<class T>
auto adl_end( T&& t )
->decltype(end(std::forward<T>(t)) )
{ return end(std::forward<T>(t)); }
}
template<class T>
auto begin(T&& t)
->decltype(std_adl::adl_begin(std::forward<T>(t)))
{ return std_adl::adl_begin(std::forward<T>(t)); }
template<class T>
auto end(T&& t)
->decltype(std_adl::adl_end(std::forward<T>(t)))
{ return std_adl::adl_end(std::forward<T>(t)); }
}

现在,当你想启用ADL时,无论是在你的has_begin类型特征中还是在使用点上,都要使用adl::begin来代替std::begin

对此:

namespace std {
void begin(foo&) {
}
}

这使得您的程序格式不正确,不需要进行诊断。