【问题标题】:How to wrap a templated function to work on const and non-const data如何包装模板函数以处理 const 和非 const 数据
【发布时间】:2013-11-23 06:56:47
【问题描述】:

我想创建一个模板化函数,该函数对 const 和非 const 数据的工作方式相同,只是它会根据需要返回一个 const 或非 const 指针。

例如,我想返回一个指向容器中匹配元素的指针:

template <class Container, class Pred>
typename Container::value_type* getValuePtrIf(Container& c, Pred pred)
{
    auto it=std::find_if(c.begin(), c.end(), pred);
    return (it!=c.end()) ? &(*it) : nullptr;
}

但是,我无法为 const 和非 const 调用构建它。如果我在 Container&amp; c 声明中省略了 const,那么它就不能返回一个 const 指针,但是如果我更改为 const Container&amp; c,那么我可以返回一个 const 指针,但是非 const 情况不会建立。

有没有一种方法来定义它,以便 const 被解释为 Container 模板参数的一部分,这样我只需要定义这个函数的一个版本?

【问题讨论】:

  • 为什么在不修改c的情况下需要非常量?
  • 我想要一个返回 const 指针的版本和一个返回非常量指针的版本
  • @MM 因为如果容器是可修改的,他想要一个指向非常量的指针作为返回值
  • C++11: template&lt;class C, class P&gt; auto getValuePtrIf(C&amp; c, P pred) -&gt; decltype( &amp;*c.begin() );
  • @DyP:做到了!请把它作为一个答案,我会接受它。这是只能使用 decltype 才能完成的事情吗?

标签: c++ c++11 constants


【解决方案1】:

最简单的就是定义两个模板,让编译器找到最佳匹配

template <class Container, class Pred>
typename const Container::value_type* getValuePtrIf(const Container& c, Pred pred)
{
    auto it=std::find_if(c.begin(), c.end(), pred);
    return (it!=c.end()) ? &(*it) : nullptr;
}
template <class Container, class Pred>
typename Container::value_type* getValuePtrIf(Container& c, Pred pred)
{
    auto it=std::find_if(c.begin(), c.end(), pred);
    return (it!=c.end()) ? &(*it) : nullptr;
}

如果您因维护问题而反对重复代码(值得反对),您可以尝试以下方法:

template <class Container, class Pred>
typename const Container::value_type* getValuePtrIf(const Container& c, Pred pred)
{
    auto it=std::find_if(c.begin(), c.end(), pred);
    return (it!=c.end()) ? &(*it) : nullptr;
}
template <class Container, class Pred>
typename Container::value_type* getValuePtrIf(Container& c, Pred pred)
{
    return const_cast<Container::value_type*>(
        getValuePtrIf(const_cast<const Container &>( c ), pred)
    );
}

如果是我,我也会将 Pred 替换为 const Pred & pred)

罗伯特·拉米

【讨论】:

  • 我提出这个问题的原因是为了避免创建两个不同的版本。我认为使用两个 const 强制转换非常不吸引人。鉴于std::find_if() 的定义本身按值取1,为什么您认为将谓词参数设为const 更好? en.cppreference.com/w/cpp/algorithm/find
  • 大声笑——但是你想要两个不同的版本!一个版本返回一种类型(const Container::value_type *),另一个版本返回另一种类型(const Container::value_type *)。可能有不同的语法来实现这一点——但它们都相当于同一件事。如果你不喜欢 const_cast - 你可以使用第一种方法。但请注意,这里使用的 const_cast 完全符合其预期 - 完全允许这种事情。通过使用它,您可以完全满足您的要求(尽管可能不是您想要的!)。
  • 我更喜欢使用 const Pred & p 而不是 Pred p 有几个原因。 a) 根据编译情况,传递我的引用可能会避免多余的副本。 b) 它允许传递一个不可复制的谓词。 c) 可变谓词可能会产生令人惊讶的效果——可能会根据 find_if 的实现方式返回不同的结果。 - 所以我会用 const 来禁止它。
【解决方案2】:

从代码看来,您的编译器支持 C++11。然后你可能也可以使用decltype 和尾随返回类型:

template <class Container, class Pred>
auto getValuePtrIf(Container& c, Pred pred) -> decltype(&*std::begin(c))
{
    auto it=std::find_if(std::begin(c), std::end(c), pred);
    return (it!=std::end(c)) ? &(*it) : nullptr;
}

it 将是std::begin(c) 提供的任何类型(iterator 或const_iterator),因此&amp;(*it) 的类型与decltype(&amp;*std::begin(c)) 的类型相同。

【讨论】:

  • 非常聪明。我第一次真正看到了让 C++ 语法比现在更复杂的动机。并不是说我认为这是一个好主意。这里的意图是非常不明显的。这不是对答案的批评,而是真正质疑使 C++ 变得比现在更复杂的想法。我质疑将这种东西添加到 C++ 中是否真的与增加的语法不透明度成比例地增加了实用性。
  • @RobertRamey 在 C++14 中,-&gt; decltype(&amp;*std::begin(c)) 将消失,因为函数的返回类型对于编译器是可推导出的。类型将相同。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多