如何将函数设置为默认参数?

时间:2017-04-23 19:48:18

标签: c++11 templates vector iterator default-value

我遇到了将函数设置为默认参数的问题。

以下代码没有多大意义。我想要实现的目标可以通过许多不同的方式实现。此代码仅描述了我遇到的问题,并希望知道如何修复它以符合我的规范。

#include <iostream>
#include <vector>

int double_the_number(int x)
{
    return x * 2;
}

template<typename T, typename FunctionType>
std::vector<FunctionType> copy_with_criteria(T iter1, T iter2, FunctionType F(FunctionType))
{
    std::vector<int> new_vector;
    while(iter1 != iter2)
    {
        new_vector.push_back(F(*iter1++));
    }
    return new_vector;
}

int main()
{
    std::vector<int> v {1,2,3,4,5};
    auto new_vector = copy_with_criteria(v.begin(), v.end(), double_the_number);
    for(int x : new_vector) std::cout << x << " ";
    return 0;
}

当上面的代码运行时,它将输出2 4 6 8 10

我想要实现的是,如果我在没有指定标准函数copy_with_criteria(v.begin(), v.end())的情况下调用函数,我希望它输出1,2,3,4,5

也就是说,我想以某种方式将函数设置为默认参数,这是一个容器内部的元素类型(在本例中为vector)并返回已发送给它的数字,如下所示(TypeOfElements只是默认条件函数应该是什么类型的一个示例):

TypeOfElements default_function(TypeOfElements x) {
    return x;
}

我不想使用任何外部库。我也在使用c ++ 11。

如果有人能帮我解决这个问题,我将非常感激!

谢谢:)

3 个答案:

答案 0 :(得分:0)

重载你的功能。

template <typename T>
std::vector<typename std::iterator_traits<T>::value_type>
copy_with_criteria(T iter1, T iter2)
{
    return std::vector<typename std::iterator_traits<T>::value_type>(iter1, iter2);
}

答案 1 :(得分:0)

您可以将您的功能定义为:

FunctionType

它按照要求在C ++ 11中工作(参见Coliru) 基本思想是您可以直接从迭代器的类型推导出F,而不是从函数F推导出来。然后你可以使用lambda函数给copy_with_criteria一个默认值,这个函数只不过是一个身份函数。

否则你可以按照aschepler的建议简单地重载iterator_traits<T>::value_type(我更倾向于使用他的方法而不是使用默认参数)或者只是定义一个具有有意义名称的不同函数明确表示你的意图是在这种副本中没有使用标准。

修改

正如@aschepler在评论中所建议的那样,您可以使用typename std::decay<decltype(*std::declval<T>())>::type代替{{1}}来避免某些类型的问题。

答案 2 :(得分:0)

在这里使用函数是错误的。国家很容易有用。您想要使用通用函数对象。并推断出返回值。

此外,使用迭代器范围是值得怀疑的。 C ++范围库的下一次迭代将减少使用。

虽然您需要C ++ 11解决方案,但没有理由使用C ++ 03 / C ++ 11 样式。前瞻性。

让我们开始吧。

#include <iterator>
namespace notstd {
  namespace adl_helper {
    using std::begin; using std::end;
    template<class C>
    auto adl_begin( C&& )
    -> decltype( begin( std::declval<C>() ) );
    template<class T, std::size_t N>
    auto adl_begin( T(*)[N] )
    -> T*;
  }
  template<class C>
  using iterator_type=decltype(
    ::notstd::adl_helper::adl_begin( std::declval<C>() )
  );
}

通过在启用ADL的上下文中调用std::begin来查找容器的迭代器类型。这模拟了for(:)循环相当好的作用。

namespace notstd {
  template<class C>
  using value_type = typename std::iterator_traits<
    iterator_type<C>
  >::value_type;
}

现在我们可以value_type<C>找到某种类型C并获取它包含的类型。

namespace notstd {
  struct make_a_copy_t {
    template<class T>
    auto operator()(T&& t)const
    -> std::decay_t<T>
    {
      return std::forward<T>(t);
    }
  };
}

make_a_copy_t是一个复制内容的仿函数。

我们已准备好解决您的问题。

template<class Op=notstd::make_a_copy_t, class C,
  class R=decltype( std::declval<Op&>()(std::declval<notstd::value_type<C&>>()) )
>
std::vector<R>
copy_with_criteria(C&& c, Op op={})
{
  std::vector<R> new_vector;
  for (auto&& e:std::forward<C>(c))
  {
     new_vector.push_back( op(decltype(e)(e)) );
  }
  return new_vector;
}

我认为这符合您的标准。

您可能还需要

namespace notstd {
  template<class It>
  struct range_t {
    It b = {};
    It e = {};
    It begin() const { return b; }
    It end() const { return e; }
    range_t( It s, It f ):b(std::move(s)), e(std::move(f)) {}
    range_t( It s, std::size_t count ):
      range_t( s, std::next(s, count) )
    {}
    range_t() = default;
    range_t(range_t&&)=default;
    range_t(range_t const&)=default;
    range_t& operator=(range_t&&)=default;
    range_t& operator=(range_t const&)=default;
    range_t without_front(std::size_t N)const {
      return {std::next(begin(), N), end()};
    }
    range_t without_back(std::size_t N)const {
      return {begin(), std::prev(end(),N)};
    }
    std::size_t size() const {
      return std::distance(begin(), end());
    }
    // etc
  };
  template<class It>
  range_t<It> range( It b, It e ) {
    return {std::move(b), std::move(e)};
  }
  template<class It>
  range_t<It> range( It b, std::size_t count ) {
    return {std::move(b), count};
  }
  template<class C>
  range_t<iterator_type<C&>> range( C& c ) {
    using std::begin; using std::end;
    return {begin(c), end(c)};
  }
}

允许您对容器的子部分进行操作作为范围。

所以假设你想要取一个int向量的前半部分并加倍。

std::vector<int> some_values{1,2,3,4,5,6,7,8,9,10};
auto front_half = notstd::range(some_values).without_back(some_values.size()/2);
auto front_doubled = copy_with_criteria( front_half, [](int x){return x*2;} );

并完成。

Live example