这是一个远景,但是我正在尝试为某些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。
答案 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()
。同样的论点仍然适用。