【问题标题】:Define a function in function declaration using std::iterator traits and auto使用 std::iterator 特征和 auto 在函数声明中定义函数
【发布时间】:2019-05-13 12:38:21
【问题描述】:

今天我尝试实现基数排序。该函数必须有两个变量:开始迭代器和结束迭代器,并且可以有第三个:一些必须返回整数类型以进行排序的函数。默认必须是恒等函数。

我的尝试看起来像(抱歉,代码看起来很长很脏,但这只是一个尝试):

template<class ForwardIt>
void radix_sort(
    ForwardIt first,
    ForwardIt last,
    std::function<auto(typename std::iterator_traits<ForwardIt>::value_type)> get_value =
    [](const typename std::iterator_traits<ForwardIt>::value_type& x){ return x; }) {
        // ...
}

get_value的返回类型,当然在编译时就知道了

用法应该是:

std::vector<std::pair<uint32_t, std::string>> vec;
// ...
radix_sort(vec.begin(), vec.end(), [](const std::pair<uint32_t, std::string>& x){ return x.first; })

或者:

std::vector<uint32_t> vec;
// ...
radix_sort(vec.begin(), vec.end());

它甚至没有编译,我不知道如何解决这个问题。怎么做?简单例子:

#include <bits/stdc++.h>

template<class ForwardIt>
void radix_sort(
    ForwardIt first,
    ForwardIt last,
    std::function<auto(typename std::iterator_traits<ForwardIt>::value_type)> get_value =
    [](const typename std::iterator_traits<ForwardIt>::value_type& x){ return x; }) {
        // ...
}

int main()
{
    std::vector<std::pair<uint32_t, std::string>> vec(10);
    radix_sort(vec.begin(), vec.end());
}

编译器输出:

source_file.cpp:17:37: error: no matching function for call to ‘radix_sort(std::vector<unsigned int>::iterator, std::vector<unsigned int>::iterator)’
     radix_sort(vec.begin(), vec.end());
                                      ^
source_file.cpp:6:6: note: candidate: template<class ForwardIt, class auto:1> void radix_sort(ForwardIt, ForwardIt, std::function<auto:1(typename std::iterator_traits<_Iter>::value_type)>)
 void radix_sort(
      ^
source_file.cpp:6:6: note:   template argument deduction/substitution failed:
source_file.cpp:17:37: note:   couldn't deduce template parameter ‘auto:1’
     radix_sort(vec.begin(), vec.end());

【问题讨论】:

  • 编译器是怎么说的? --> 只需将编译器错误/警告/输出添加到您的帖子中,如果不是为了其他原因,那么只是为了完整性。
  • 请参阅How to Ask 以改进您的问题
  • 我认为模板中的 仅在您拥有 C++17 并且问题标记为 C++11 时才有效。见this answer。可能还有其他问题我没有花时间仔细分析问题,但我会从可用的标准开始。
  • @Clonk:实例化模板时,auto 不起作用。
  • @JoeyMallone 在您使用它的同一个 .cpp 文件中实现模板没有问题。当您想在多个不同的包含标头的 .cpp 文件中使用模板时,您只需要在标头中实现它们。模板不能在 .cpp 文件中没有任何内在原因。

标签: c++ c++11


【解决方案1】:

解决此问题的简单方法是不使用默认函数,而是使用两个重载。这可以让您摆脱使用昂贵的std::function,代价是编写几行样板代码。如果你使用

template<class ForwardIt, class Func>
void radix_sort(ForwardIt first, ForwardIt last, Func get_value) {
    // ...
}

template<class ForwardIt>
void radix_sort(ForwardIt first, ForwardIt last) {
    radix_sort(first, last, [](const typename std::iterator_traits<ForwardIt>::value_type& x){ return x; });
}

您将获得没有功能的默认“身份”,如果提供了一个,您将获得确切的功能对象。

【讨论】:

  • 这是否意味着我不能如我所愿地给出默认值?
  • 这就是第二个重载为您所做的。如果不指定第三个参数,则会得到第二个重载,而 that 定义了默认值。
  • @AlexanderStanovoy 默认提供了第二个重载。如果您只使用 2 个迭代器调用它,您将获得第二个使用默认标识函数的函数。如果用户提供了自己的函数对象,那么它将调用第一个并使用他们提供的。
  • 请注意,lambda 将按值返回,这可能是不可取的
  • 如果这是一个问题,可以将 lambda 更改为 [](const typename std::iterator_traits&lt;ForwardIt&gt;::value_type&amp; x) -&gt; decltype(auto) { return (x); })
【解决方案2】:

为了未来用户的利益, 我想指出的是,C++20 引入了 std::identity 类, 这有助于解决问题。 在它的帮助下, 代码可以改写为:

template <typename For, typename F = std::identity>
void radix_sort(For first, For end, F f = {})
{
  /* ... */
}

而且很容易自己实现一个符合标准的 如果你没有 C++20,像这样:

struct identity {
  template <class T>
  constexpr T&& operator()(T&& t) const noexcept
  {
    return std::forward<T>(t);
  }

  using is_transparent = void;
};

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-12-20
    • 1970-01-01
    • 2013-03-13
    • 1970-01-01
    • 2014-01-17
    • 1970-01-01
    • 2020-10-07
    相关资源
    最近更新 更多