考虑以下类型特征:
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.
}
有趣的观察:
- 如果我更改此类型特征和重载的顺序,它就会起作用
- 如果我在类型特征中使用 ADL:
decltype(std::begin(std::add_lva... -> decltype(begin(std::add_lva...
如果自由函数 begin
与 foo 位于同一命名空间中,则它可以工作:
void begin(foo) {
}
但对于 std:: 之外的任何类都会失败,具体取决于:
template<class C>
auto begin(C& c) -> decltype(c.begin());
因为 ADL 查找不适用于其他命名空间的模板。
<小时/>如何在不更改包含顺序的情况下支持类型特征中的 std::begin(foo&)
?
否则我必须支持两个世界 - 为 std::begin 和 ADL begin() 编写类型特征...
在我的函数中,我已经做了类似的事情(建议 here ):
auto get_begin() {
using std::begin;
return begin(object);
}
最佳答案
What could I do to support std::begin(foo&) in my type trait without changing the include order?
你没有; std::begin
并不意味着直接调用任意范围。如果您想访问范围类型的 begin/end
,则应该使用 ADL,并结合 using std::begin/end
。这正是该习惯用法在 C++ 中的工作原理。
在 std
命名空间中重载方法是非法的,std::begin
也不异常(exception)。您可以创建 std
定义模板的模板特化(基于用户创建的类型),但这不是使用 C++ 习惯用法的正确方法。
在 C++20 中,std::ranges::begin
函数应该直接调用,而将其专门化为类型的方式是通过 ADL 或成员 开始
函数。所以用这个成语就行了,大家就都没事了。
关于c++ - std::begin - 类型特征中不考虑用户定义的重载,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58894939/