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

时间:2013-12-28 23:33:48

标签: c++ templates c++11

如何为以下函数T获取正确的to_vector类型?

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

5 个答案:

答案 0 :(得分:6)

您可以使用

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

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

答案 1 :(得分:5)

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

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>

答案 2 :(得分:4)

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

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); 
}

答案 3 :(得分:0)

可以使用特征:

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);

答案 4 :(得分:0)

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

您应该通过在其命名空间中重载AB来将类型beginend转换为可迭代对象。

有多种方法:

1)您强制执行每个成员 - begin和成员 - end,它将指针作为迭代器返回。或者,自由函数beginend执行相同的操作。

2)您要求他们从执行上述操作的CRTP帮助程序类继承 - 它要么为您实现beginend,要么启用自由函数begin和使用ADL可以看到end重载。

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类,通过检查n_p_iterable类型为T::n和{{1}来检测它是否应为std::size_t是指针类型。我建议不要这样做,因为虽然它在其他地方需要较少的样板,但它非常黑客。

一旦我们有了这个,我们就可以编写一个非常通用的T::p

首先,我们自己写一个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 s,那么现在你的类型 a {{{{{{ 1}}适用于您的类型和其他可迭代容器(例如t)。

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