如何为以下函数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);
这是一些较大代码的一部分,还有更多类型,如A
和B
。但所有都有指针p
和长度n
。
答案 0 :(得分:6)
您可以使用
typename std::decay<decltype(*X::p)>::type
代表T
,因为decltype
是一个未评估的上下文,因此X::p
在这里是合法的。此外,std::decay
似乎很合适,因为它将std::remove_reference
与std::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)
所以这里完全是左边的一个答案。
您应该通过在其命名空间中重载A
和B
来将类型begin
和end
转换为可迭代对象。
有多种方法:
1)您强制执行每个成员 - begin
和成员 - end
,它将指针作为迭代器返回。或者,自由函数begin
和end
执行相同的操作。
2)您要求他们从执行上述操作的CRTP帮助程序类继承 - 它要么为您实现begin
和end
,要么启用自由函数begin
和使用ADL可以看到end
重载。
3)如果所有这些类都在您控制的某些namespace
中,并且您希望将K* p
和std::size_t n
字段视为应将其视为可迭代范围的证据,然后我们可以使用“全局”begin
和end
来使用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
中的大小。但这篇文章足够长了。