有没有办法将模式包装成通用的模板函数?
template <typename C>
auto Begin(C&& c) -> ??? {
using std::begin;
return begin(std::forward<C>(c));
}
这里的问题是这里如何编写函数的返回类型?
我想要这个的原因是我想写一个模板变量
template <typename C>
constexpr bool IsBidirectionalContainer =
std::is_base_of<std::bidirectional_iterator_tag,
typename std::iterator_traits<
decltype(std::begin(std::declval<C>()))>::iterator_category>::value;
这里的问题是std::begin
不会找到通过 ADL 进行C
的自定义begin
重载。如果有人对此有解决方法,也欢迎。
你需要把它包装在另一个命名空间中,即:
namespace details {
using std::begin;
template <typename C>
auto Begin(C&& c) -> decltype(begin(std::forward<C>(c)))
{
return begin(std::forward<C>(c));
}
}
然后:
template <typename C>
constexpr bool IsBidirectionalContainer =
std::is_base_of<std::bidirectional_iterator_tag,
typename std::iterator_traits<
decltype(details::Begin(std::declval<C>()))>::iterator_category>::value;
如果出于某种原因您拒绝在命名空间中定义Begin
,则可以使用类型别名来绕过它。
namespace details {
using std::begin;
template <typename C>
using type = decltype(begin(std::forward<C>(c)));
}
template <typename C>
auto Begin(C&& c) -> details::type<C>
{
return begin(std::forward<C>(c));
}
尽管这可能比必要的工作要多。向前声明可能就足够了。
如果你要包装它,我想"为什么不使用最新的最好的想法呢? 具体来说,Eric Niebler 提出了一个论点,即这些应该是函数调用对象,而不是函数!
这是我使用 C++17 编译器的新版本
// ===================
// would be in a reusable header
namespace twostep {
using std::begin;
using std::end;
inline auto Begin = [](auto&& r) -> decltype(begin(std::forward<decltype(r)>(r))) {
return begin(std::forward<decltype(r)>(r));
};
inline auto End = [](auto&& r) -> decltype(end(std::forward<decltype(r)>(r))) {
return end(std::forward<decltype(r)>(r));
};
}
using twostep::Begin;
using twostep::End;
现在,由于这不会取代std::begin
/end
函数的实现,因此我无法获得 Eric 指出的所有好处。 但是,除了充当为我执行两步的包装器之外,就像普通模板函数实现一样,它本身不受 ADL 的影响。
如果我提到有资格twostep::Begin
,那并不重要。 但是,如果我像此列表对全局范围所做的那样将它们导入到我自己的范围内,那么对Begin(r)
的非限定调用肯定会看到我此时可见的调用,并且由于r
中涉及的所有类型,在不同的命名空间中找不到名为Begin
的函数。