c++中std::reference_wrapper和types的编译错误

Compiler error with std::reference_wrapper and types in C++

本文关键字:types 编译 错误 std reference c++ wrapper      更新时间:2023-10-16

我最近一直在钻研c++,我一直在尝试STL。我遇到了一个我似乎无法解决的问题。我猜这是因为我不理解c++是如何推断类型的,但我不确定。下面是运行

的代码
template <typename T>
auto most_frequent_element(const std::vector<std::reference_wrapper<T>> &vector) -> boost::optional<std::pair<const std::reference_wrapper<T>, size_t>> {
    if (vector.empty()) {
        return{};
    }
    std::map<std::reference_wrapper<T>, size_t> individual_counts = {};
    std::for_each(vector.begin(), vector.end(), [&individual_counts](auto &element) {
        auto result = individual_counts.emplace(element, 0);
        (*result.first).second++;
     });
    return *std::max_element(individual_counts.begin(), individual_counts.end(), [](auto &a, auto &b) {
        return a.second < b.second;
    });
}

我可以这样命名它

auto main(int argc, char *argv[]) -> int {
    std::vector<char> test = { 'a', 'a', 'b', 'b', 'c'};
    auto result = most_frequent_element(std::vector<std::reference_wrapper<char>>(test.begin(), test.end()));
    if (result) {
        std::cout << (*result).first << " " << (*result).second << std::endl;
    }
    else {
        std::cout << "Empty input list." << std::endl;
    }
    return EXIT_SUCCESS;
}

这是可行的。但是如果我创建一个简单的方法来消除手动复制矢量的需要,就像这样

template <typename T>
auto most_frequent_element_2(const std::vector<T> &vector) -> boost::optional<std::pair<const std::reference_wrapper<T>, size_t>> {
    most_frequent_element(std::vector<std::reference_wrapper<T>>(vector.begin(), vector.end()));
}

,像这样命名

auto main(int argc, char *argv[]) -> int {
    std::vector<char> test = { 'a', 'a', 'b', 'b', 'c'};
    auto result = most_frequent_element_2(test);
    if (result) {
        std::cout << (*result).first << " " << (*result).second << std::endl;
    }
    else {
        std::cout << "Empty input list." << std::endl;
    }
    return EXIT_SUCCESS;
}

我得到以下错误

Error C2664 'std::reference_wrapper<char>::reference_wrapper(std::reference_wrapper<char> &&)': cannot convert argument 1 from 'const char' to 'char &'

我很困惑,因为我怎么看他们应该做同样的事情。如果我错了,有人能给我指个方向吗?

注:我在Visual Studio 2015中工作

更新:为函数的返回类型添加了const约束,以正确反映*max_element返回const键的事实。(由@dyp建议)

问题与连续性有关。最初,从

的隐式转换存在一个问题
optional<pair<const reference_wrapper<T>, size_t>> // A

optional<pair<reference_wrapper<T>, size_t>> // B

第一个是const,因为std::map节点的键是const——防止键的突变将确保树数据结构保持有效。

这个隐式转换是不允许的;这可能与改进pairtuple - N4387中描述的std::pair问题有关。

一个快速修复是使用A作为返回类型,而不是B。或者,使用显式转换(指定对象的强制转换或构造)。


与连续性相关的第二个问题是

reference_wrapper<T>

不能用

初始化
T const&

就像T&不能从T const类型的左值表达式初始化一样。出现这个问题是因为most_frequent_element_2函数接受const&作为参数,然后调用beginend产生const_iterator s。

一个快速而肮脏的解决方案是创建一个vector<reference_wrapper<T const>>:

most_frequent_element(std::vector<std::reference_wrapper<T const>>(vector.begin(), vector.end()));
//                                                         ^^^^^

然后调整_2函数的返回类型为

boost::optional<std::pair<const std::reference_wrapper<T const>, size_t>>
//                                                       ^^^^^

现场的演示

一个更好的解决方案可能是认识到_2函数不需要vector,而只需要两个迭代器来创建新的vector对象。(同样的参数也适用于原始的most_frequent_element函数。)你可以这样写:

template <typename InIt,
          typename T = std::remove_reference_t<decltype(*std::declval<InIt&>())>>
auto most_frequent_element_2(InIt b, InIt e)
  -> boost::optional<std::pair<const std::reference_wrapper<T>, size_t>> {
    return most_frequent_element(std::vector<std::reference_wrapper<T>>(b, e));
}

,我使用第二个(默认的)模板参数只是为了方便(声明中的typedef)。

现场演示