通用引用,推断std :: list <t>和T

时间:2018-08-17 02:08:40

标签: c++ templates c++17

这是一个远景,但是我正在尝试为某些std::list<T>推断类型为T的通用引用。

我有这样的东西:

// Is the type A the same as B, regardless of const and references
template <typename A, typename B>
struct is_same_kind {
    static constexpr auto value = std::is_same_v<std::remove_cv_t<std::remove_reference_t<A>>,
                                                 std::remove_cv_t<std::remove_reference_t<B>>>;
};

template <typename A, typename B>
static constexpr auto is_same_kind_v = is_same_kind<A,B>::value;

template<typename L, typename T, typename = std::enable_if_t<is_same_kind_v<L, std::list<T>>>>
T head(L&& l) {
   return *std::forward<L>(l).begin();
}

由于预处理器无法推断T,因此出现错误。也许有一些不错的技巧可以从参数L&&推断std::list<T>作为对T的通用引用和类型l的推论?

编辑:例如,这是调用它的方法:

int main() {
    std::cout << head(std::list{1,2,3});
}

我希望得到1。

2 个答案:

答案 0 :(得分:1)

好的,我想我已经找到了一种默认使用value_type成员的方法。我先打电话给remove_reference_t<L>,因为L& :: value_type如果通过左值引用传递,将无法正常工作。

template<typename L, typename T = typename std::remove_reference_t<L>::value_type, typename = std::enable_if_t<is_same_kind_v<L, std::list<T>>>>
T head(L&& l) {
   return *std::forward<L>(l).begin();
}

int main() {
   std::cout << head(std::list{1,2,3});
}

答案 1 :(得分:1)

这不是对原始问题的解答,而是关于函数实现的重要一点。您使用捕获的值的方式不正确。对于int来说并不重要,因为没有什么可移动的,但是对于某些类型来说确实很重要(尤其是对于L&&真正有意义的类型)。例如:

template<typename L>
auto head(L&& l) {
    return *std::forward<L>(l).begin();
}

int main() {
    std::list<std::unique_ptr<int>> list;
    list.push_back(std::make_unique<int>(1));
    list.push_back(std::make_unique<int>(2));

    auto z = head(std::move(list));
    return 0;
}

该代码甚至无法编译。由于std::unique_ptr并未移出,因此这里正在尝试制作*it的副本。

首先,对转发的值调用begin()毫无意义。编译器唯一的选择是调用begin()begin() const,并且此选择不依赖于l的值类别。如果在右值上调用std::move_iterator,则生成的迭代器不会自动成为begin()。其次,如果将右值传递到函数中,则应std::move中的*it。因此,head(...)函数可能看起来像这样(使用一些不错的C ++ 17功能):

template<typename L>
auto head(L&& l) {
    if constexpr (std::is_rvalue_reference_v<L&&>)
        return std::move(*l.begin());
    else
        return *l.begin();
}

我们可以使用*l.begin()代替l.front()。同样的论点仍然适用。