如何获取函数返回类型的模板成员的取消引用类型



如何获得以下函数to_vector的正确类型T

template<typename K> struct A { K* p; size_t n; std::string foo; };
template<typename K> struct B { K* p; size_t n; float bar[3]; };
template<typename X>
std::vector<T> to_vector(const X& x) {   // what is T?
return std::vector<T>(x.p, x.p+x.n); 
}

我尝试使用decltype(*std::declval<X>().p)但这会导致以下示例error: forming pointer to reference type ‘float&’

A<float> a = { new float[10], 10, "hello" };
std::vector<float> v = to_vector(a);

这是一些更大代码的一部分,还有更多类型,如AB.但是都有一个指针p和一个长度n.

你可能可以使用

typename std::decay<decltype(*X::p)>::type

T因为decltype是一个未评估的上下文,因此X::p在这里是合法的。此外,std::decay似乎很合适,因为它结合了std::remove_referencestd::remove_cv.

你走在正确的轨道上,你只需要使用适当的实用程序来摆脱指针/引用。

std::remove_reference,您可以像这样使用:

typename std::remove_reference<decltype(*std::declval<X>().p)>::type

但是std::remove_pointer起来更简单一些:

typename std::remove_pointer<decltype(std::declval<X>().p)>::type

(看,*消失了,否则相同)。

如果指针可能符合cv条件,您可能希望将 std::remove_cv 放入混合中,因为矢量元素不应该是。


如另一个现已删除的答案中所述,如果您使用尾随返回类型声明,则可以编写x.p而不是std::declval<X>().p

template <typename X>
auto to_vector(const X& x) ->
std::vector<typename std::remove_pointer<decltype(x.p)>::type>

这个答案现在几乎已经过时了。

template<typename X, typename T = typename std::remove_reference<decltype(*X().p)>::type>
std::vector<T> to_vector(const X& x)
{
return std::vector<T>
(x.p, x.p+x.n); 
}

所以这是一个完全超出左边的答案。

应通过将beginend的命名空间中重载和BA类型转换为可迭代对象。

有多种方法可以做到这一点:

1) 强制它们中的每一个都实现一个成员begin和成员end,该成员返回指针作为迭代器。 或者,一个自由函数beginend做同样的事情。

2) 你要求它们继承自执行上述操作的 CRTP 帮助程序类 - 它要么为你实现beginend,要么启用一个自由函数beginendADL 可以看到的重载。

3)如果所有这些类都在您控制的某个namespace中,并且您希望将K* pstd::size_t n字段视为应将其视为可迭代范围的证据,那么我们可以使用"全局"beginend来执行此操作,该使用SFINAE仅适用于该情况。

我会建议#1或#2。

对于#2:

template<typename Derived>
struct p_n_iterable {
Derived* self() {
static_assert( std::is_base_of<p_n_iterable, Derived>::value, "CRTP failure" );
return static_cast<Derived*>(this);
}
Derived const* self() const {
static_assert( std::is_base_of<p_n_iterable, Derived>::value, "CRTP failure" );
return static_cast<Derived const*>(this);
}
typedef typename std::decay< decltype( *Derived::p ) >::type value_type;
typedef value_type* iterator;
std::size_t size() const { return self()->n; }
iterator begin() { return self->p; }
iterator end() { return begin() + size(); }
};

如果我写的没错,请将A改为:

template<typename K> struct A : p_n_iterable<A<K>> { ... unchanged ... };

突然之间,for( auto x:a )型循环在A上工作.

我认为必须将小序言添加到类中的成本对于该功能来说是值得的。

为了使用 #3 执行此操作,我们创建了一个traits类,通过检查T::n是否属于std::size_t类型以及T::p是指针类型来检测是否应该n_p_iterable它。 我建议不要这样做,因为虽然它在其他地方需要较少的样板,但它非常笨拙。

一旦有了这个,我们就可以写一个非常通用的to_vector.

首先,我们给自己写一个get_iterator_type<Container>

namespace adl_helper {
using std::begin; using std::end;
template<typename C>
auto adl_begin(C&& c)->decltype(begin( std::forward<C>(c) ));
template<typename C>
auto adl_end(C&& c)->decltype(end( std::forward<C>(c) ));
}
using adl_helper::adl_begin;
using adl_helper::adl_end;
template<typename... Ts> struct type_sink { typedef void type; }
template<typename... Ts> using TypeSink = typename type_sink<Ts...>::type;
template<typename Container, typename=void>
struct get_iterator_type {};
template<typename Container>
struct get_iterator_type< Container, TypeSink< adl_begin( std::declval<Container&>() ) > > {
typedef adl_begin( std::declval<Container&>() ) type;
};
template<typename Container, typename=void>
struct get_value_type {};
template<typename Container>
struct get_value_type< Container, TypeSink< std::iterator_traits< typename get_iterator_type<Container>::type > > > {
typedef std::iterator_traits< typename get_iterator_type<Container>::type > > traits;
typedef typename traits::value_type type;
};

现在,我们写下我们的to_vector

template<typename C>
auto to_vector( C&& container )->
std::vector<typename get_value_type<typename remove_reference<C>::type>::type>
{
std::vector<typename get_value_type<typename remove_reference<C>::type>::type> retval;
for( auto&& x : std::forward<C>(container) ) {
retval.push_back(x);
}
return retval;
}

如果我点缀所有i并交叉所有t,您现在既有完整的 C++11 样式迭代,又有适用于您的类型和其他可迭代容器(例如std::map)的to_vector

进一步的改进可以包括检测传入的容器是否具有size或具有随机访问迭代器,如果是,则保留retval的大小。 但是这篇文章已经足够长了。

可以使用特征:

template<typename K> struct A { K* p; size_t n; std::string foo; typedef K my_type; };
template<typename K> struct B { K* p; size_t n; float bar[3]; typedef K my_type; };
template<typename X>
std::vector<typename X::my_type> to_vector(const X& x) {  
return std::vector<typename X::my_type>(x.p, x.p+x.n);
}
A<float> a = { new float[10], 10, "hello" };
std::vector<float> v = to_vector(a);

最新更新