提问人:Matthew Dodd 提问时间:6/30/2023 最后编辑:GilesDMiddletonMatthew Dodd 更新时间:6/30/2023 访问量:78
为什么使用 find_first_or_default 函数扩展 std 命名空间会阻止模板推导工作
Why does extending the std namespace with a find_first_or_default function stop template deduction from working
问:
以下函数无法编译(ISO C++ 17 标准,Visual Studio 2019)
namespace std
{
// Find the first item which matches the predicate. Returns a default constructed item (probably nullptr) on failure.
template <class Container, typename Predicate, typename T = typename std::iterator_traits<Container::iterator>::value_type>
T find_first_or_default(const Container& container, Predicate predicate, T defaultVal = T())
{
auto it = std::find_if(container.begin(), container.end(), predicate);
if (it == container.end())
return defaultVal;
return *it;
}
};
void Test()
{
std::vector<int> vec = { 1,2,3 };
int i = std::find_first_or_default(vec, [](int val) {return val % 2 == 0; });
}
如果我将命名空间更改为“std”以外的任何内容,它确实会编译。 我假设 std 命名空间内的其他东西使模板推导失败。
以下是错误:
Error C2783 'T std::find_first_or_default(const Container &,Predicate,T)': could not deduce template argument for 'T'
Error C2672 'std::find_first_or_default': no matching overloaded function found
答:
2赞
Jan Schultke
6/30/2023
#1
如注释中所述,不允许向命名空间添加任何添加符号,只有在明确允许的情况下才能专化某些类。
您的程序格式不正确,但这不是它无法编译的原因。std
在:
typename T = typename std::iterator_traits<Container::iterator>::value_type
::iterator
依赖于 template 参数,因此除非消除歧义,否则它将被解释为静态数据成员。替换将失败,无法推断,并且您无法调用该函数。要解决此问题,请使用消歧器:Container
T
typename
typename T = typename std::iterator_traits<typename Container::iterator>::value_type
如果你经常编写这样的代码,那么定义一个类似于以下内容的便利别名也可能是有意义的:std::ranges::range_value_t
// note 1: we are not guaranteed to have a T::iterator alias, e.g. when T is an array
template <typename T>
using range_iter_t = decltype(std::begin(std::declval<T>()));
template <typename T>
using range_value_t = typename std::iterator_traits<range_iter_t<T>>::value_type;
// used like:
// note 2: conventional name is 'Range', not 'Container'
// not every range is a container
template <class Range, typename Predicate, typename T = range_value_t<Range>>
// note 3: use perfect forwarding for r and and predicate because of one-time use
// in the algorithm. std::ranges::find_if also takes Range&&
T find_first_or_default(Range&& r, Predicate&& predicate, T defaultVal = T())
{
// note 4: avoid calling end() twice, end() isn't always trivial
// note 5: use std::begin/std::end to make this function work with C-style arrays
auto end = std::end(r);
// note 6: perfectly forward the predicate, don't just copy
auto it = std::find_if(std::begin(r), end, std::forward<Predicate>(predicate));
if (it == end)
return defaultVal;
return *it;
}
// note 7: it would also make sense to have a second overload which takes two
// iterators, not just a function that takes a range
评论
0赞
Matthew Dodd
6/30/2023
很棒(和彻底)的帖子,非常感谢。我将实施这些更改。
评论
std
typename std::iterator_traits<typename Container::iterator>::value_type
通过此修改,它可以在namespace std