C++ 使用std::iterator traits和auto在函数声明中定义函数

C++ 使用std::iterator traits和auto在函数声明中定义函数,c++,c++11,C++,C++11,今天我尝试实现基数排序。该函数必须有两个变量:begin iterator和end iterator,并且可以有第三个变量:必须返回整数类型进行排序的函数。默认情况下,它必须是标识函数 我的尝试看起来像(对不起,代码看起来很长很脏,但这只是一次尝试): 模板 无效基数排序( 首先, 最后呢,, std::函数get_值= [](const typename std::iterator_traits::value_type&x){return x;}){ // ... } 当然,get_值的返回

今天我尝试实现基数排序。该函数必须有两个变量:begin iterator和end iterator,并且可以有第三个变量:必须返回整数类型进行排序的函数。默认情况下,它必须是标识函数

我的尝试看起来像(对不起,代码看起来很长很脏,但这只是一次尝试):

模板
无效基数排序(
首先,
最后呢,,
std::函数get_值=
[](const typename std::iterator_traits::value_type&x){return x;}){
// ...
}
当然,get_值的返回类型在编译时是已知的

使用方法应为:

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::向量向量向量机;
// ...
基数排序(vec.begin(),vec.end(),[](const std::pair&x){return x.first;})
或:

std::向量向量向量机;
// ...
基数排序(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());
}
#包括
模板
无效基数排序(
首先,
最后呢,,
std::函数get_值=
[](const typename std::iterator_traits::value_type&x){return x;}){
// ...
}
int main()
{
std::vec(10);
基数排序(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());
source_file.cpp:17:37:错误:没有用于调用“基数排序(std::vector::iterator,std::vector::iterator)”的匹配函数
基数排序(vec.begin(),vec.end());
^
source_file.cpp:6:6:注:候选:模板无效基数排序(ForwardIt,ForwardIt,std::function)
无效基数排序(
^
source_file.cpp:6:6:注意:模板参数扣除/替换失败:
source_file.cpp:17:37:注意:无法推断模板参数“auto:1”
基数排序(vec.begin(),vec.end());

解决此问题的简单方法是不使用默认函数,而是使用两个重载。这可以让您摆脱使用
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; });
}
模板
无效基数排序(ForwardIt first、ForwardIt last、Func get_值){
// ...
}
模板
无效基数排序(先转发,后转发){
基数_排序(第一,最后,[](常量typename std::迭代器_traits::value_type&x){return x;});
}

如果没有函数,您将获得默认的“标识”,如果没有函数,您将获得确切的函数对象。

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

template <typename For, typename F = std::identity>
void radix_sort(For first, For end, F 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;
};
结构标识{
模板
constexpr T&&operator()(T&&T)const noexcept
{
返回std::向前(t);
}
使用透明=无效;
};

编译器怎么说?-->只需将编译器错误/警告/输出添加到您的帖子中,如果不是为了其他原因,那么只是为了完整性。我认为,请查看以改进您的问题。可能还有其他问题。我没有花时间仔细分析这个问题,但我会从可用的标准开始。@Clonk:实例化模板时,
auto
不起作用。@Joey Mallone在使用模板的同一个.cpp文件中实现模板没有问题。如果要在多个不同的.cpp文件(包括标头)中使用模板,则只需在标头中实现它们。没有内在的原因可以使用模板't不在.cpp文件中。这是否意味着我不能像我所希望的那样给出默认值?这就是第二个重载为您所做的。如果您不指定第三个参数,则会得到第二个重载,这定义了默认值。@AlexanderStanovoy默认值随第二个重载一起提供。如果您只使用两个迭代器调用它,则会设置使用默认标识函数的第二个函数。如果用户提供自己的函数对象,则它将调用第一个函数对象并使用提供的函数对象。请注意,lambda将按值返回,这可能是不可取的。如果这是一个问题,则lambda可以更改为
[](const typename std::iterator_traits::value_type&x)->decltype(auto){return(x);})
template <typename For, typename F = std::identity>
void radix_sort(For first, For end, F f = {})
{
  /* ... */
}
struct identity {
  template <class T>
  constexpr T&& operator()(T&& t) const noexcept
  {
    return std::forward<T>(t);
  }

  using is_transparent = void;
};