[英]Clarification on member function template specialization using enable_if
我想了解在尝试最大程度地减少成员函数模板专业化的冗长性时出了错。 这样做时会出现编译错误。 这是有效的版本,希望可以阐明我要实现的目标:
#include <iostream>
#include <type_traits>
typedef int i32;
template<class T>
struct rtvec
{
private:
T* e;
i32 d;
public:
rtvec(i32 d) : d(d), e(new T[d]) {}
//template<typename Args...>
//rtvec()
rtvec(const rtvec& in) : d(in.d), e(new T[in.d])
{
for (i32 i = 0; i < d; ++i)
at(i) = in.at(i);
}
rtvec(rtvec<typename std::remove_pointer_t<T>>& in) : d(in.dim()), e(new T[in.dim()])
{
for (i32 i = 0; i < d; ++i)
e[i] = &in.at(i);
}
~rtvec() { delete[] e; }
i32 dim() const { return d; }
template<typename U=T,
typename std::enable_if_t<std::is_same_v<U,T>>* = nullptr,
typename std::enable_if_t<!std::is_pointer_v<U>>* = nullptr>
inline T& at(i32 i)
{
return e[i];
}
template<typename U = T,
typename std::enable_if_t<std::is_same_v<U, T>>* = nullptr,
typename std::enable_if_t<std::is_pointer_v<U>>* = nullptr>
inline typename std::remove_pointer_t<T>& at(i32 i)
{
return *e[i];
}
};
int main()
{
rtvec<float> v(2);
v.at(0) = 1;
v.at(1) = 2;
rtvec<float*> p = v;
p.at(0) = 5;
std::cout << v.at(0) << " " << v.at(1) << "\n";
return 0;
}
基本上,我试图制作一个运行时变量维向量类,当使用指针实例化该向量类时,该类可以用作对相同类型向量的一种引用(更确切地说,我有一组集合的每个坐标的多个数组)点,我想使用“参考”向量来处理这些点,就好像它们在内存中以其他方式排序一样。 但是,当我尝试简化代码时,通过尝试删除我认为不必要的typename U
。 我在MSVC2017中收到以下编译错误: std::enable_if_t<false,void>' : Failed to specialize alias template
。 这是我要达到的不太冗长的版本:
struct rtvec
{
private:
T* e;
i32 d;
public:
rtvec(i32 d) : d(d), e(new T[d]) {}
template<typename std::enable_if_t<!std::is_pointer_v<T>>* = nullptr>
rtvec(const rtvec& in) : d(in.d), e(new T[in.d])
{
for (i32 i = 0; i < d; ++i)
at(i) = in.at(i);
}
template<typename std::enable_if_t<std::is_pointer_v<T>>* = nullptr>
rtvec(rtvec<typename std::remove_pointer_t<T>>& in) : d(in.dim()), e(new T[in.dim()])
{
for (i32 i = 0; i < d; ++i)
e[i] = &in.at(i);
}
~rtvec() { delete[] e; }
i32 dim() const { return d; }
template<typename std::enable_if_t<!std::is_pointer_v<T>>* = nullptr>
inline T& at(i32 i)
{
return e[i];
}
template<typename std::enable_if_t<std::is_pointer_v<T>>* = nullptr>
inline typename std::remove_pointer<T>::type& at(i32 i)
{
return *e[i];
}
};
但是,如果我稍加修改,它就会编译:
template<class T>
struct rtvec
{
private:
T* e;
i32 d;
public:
rtvec(i32 d) : d(d), e(new T[d]) {}
template<typename std::enable_if_t<!std::is_pointer_v<T>>* = nullptr>
rtvec(const rtvec& in) : d(in.d), e(new T[in.d])
{
for (i32 i = 0; i < d; ++i)
at(i) = in.at(i);
}
/*template<typename std::enable_if_t<std::is_pointer_v<T>>* = nullptr>
rtvec(rtvec<typename std::remove_pointer_t<T>>& in) : d(in.dim()), e(new T[in.dim()])
{
for (i32 i = 0; i < d; ++i)
e[i] = &in.at(i);
}*/
~rtvec() { delete[] e; }
i32 dim() const { return d; }
template<typename std::enable_if_t<!std::is_pointer_v<T>>* = nullptr>
inline T& at(i32 i)
{
return e[i];
}
/*template<typename std::enable_if_t<std::is_pointer_v<T>>* = nullptr>
inline typename std::remove_pointer<T>::type& at(i32 i)
{
return *e[i];
}*/
};
(只要与指针有关的部分也在main中被注释掉)。 我想了解是什么使第二代码无法编译。
我没有直接对类进行专门化的原因是,在我的原始实现中,我有很多其他成员函数在两个专门化之间是等效的,我不想重复。
但是,当我尝试简化代码时,通过尝试删除我认为不必要的内容
不幸的是(如果我理解正确),您已删除了一些必要的内容
如果我理解正确,则您简化了以下方法
template<typename U=T,
typename std::enable_if_t<std::is_same_v<U,T>>* = nullptr,
typename std::enable_if_t<!std::is_pointer_v<U>>* = nullptr>
inline T& at(i32 i)
{
return e[i];
}
template<typename U = T,
typename std::enable_if_t<std::is_same_v<U, T>>* = nullptr,
typename std::enable_if_t<std::is_pointer_v<U>>* = nullptr>
inline typename std::remove_pointer_t<T>& at(i32 i)
{
return *e[i];
}
如下
template<typename std::enable_if_t<!std::is_pointer_v<T>>* = nullptr>
inline T& at(i32 i)
{
return e[i];
}
template<typename std::enable_if_t<std::is_pointer_v<T>>* = nullptr>
inline typename std::remove_pointer<T>::type& at(i32 i)
{
return *e[i];
}
不幸的是SFINAE仅在基于模板方法的模板参数使用测试( std::enable_if_t
)时才有效。
我的意思是:当SFINAE不工作std::enable_if_t
测试涉及T
(只有T
),因为T
是结构的模板参数,而不是方法的模板参数。
所以你需要把戏
typename U = T
在方法的模板参数中“转换” T
类型。
您可以简化一点取出typename
前std::enable_if_t
因为“ _t
”正是typename
(见定义std::enable_if_t
)
离题:我不是语言层,但据我所知,
std::enable_if_t<std::is_same_v<U,T>>* = nullptr
不完全合法; 我建议用int
代替void *
std::enable_if_t<std::is_same_v<U,T>, int> = 0
也许是bool
std::enable_if_t<std::is_same_v<U,T>, bool> = true
或其他整数类型
最后,我建议如下重写at()
方法
template <typename U = T,
std::enable_if_t<std::is_same_v<U, T>, int> = 0,
std::enable_if_t<!std::is_pointer_v<U>, int> = 0>
inline T& at(i32 i)
{
return e[i];
}
template<typename U = T,
std::enable_if_t<std::is_same_v<U, T>, int> = 0,
std::enable_if_t<std::is_pointer_v<U>, int> = 0>
inline typename std::remove_pointer_t<T>& at(i32 i)
{
return *e[i];
}
有多种原因,您需要提供完整的编译错误。 关于您的代码,您似乎使用C ++ 17。
例如,在这段代码中,您尝试返回对存储在数组中的对象的引用,对吗?
inline typename std::remove_pointer<T>::type& at(i32 i) {
return *e[i];
}
可以替换为更像STL的代码:
using reference = T&;
using const_reference = const T&;
reference at(i32 i) {
return e[i];
}
const_reference at(i32 i) const {
return e[i];
}
或使用auto
:
auto at(i32 i) const {
return e[i];
}
这是大多数STL容器的工作方式。 例如,如果您访问std::vector<T*>
,它将返回对T *的引用,而不是对T指向的数据的引用。
关于您使用的SFINAE技术,我不确定它是否编写正确。
例如,看一下这篇文章,以查找有关编写选择构造函数条件的正确方法的信息。 小总结:
template <typename = typename std::enable_if<... condition...>::type>
explicit MyAwesomeClass(MyAwesomeClass<otherN> const &);
例如,如果您只想为那些不包含指针类型的实例启用构造函数:
template<typename = typename std::enable_if_t<!std::is_pointer_v<T>>>
explicit rtvec(const rtvec& in) : d(in.d), e(new T[in.d]) {
for (i32 i = 0; i < d; ++i)
at(i) = in.at(i);
}
现在,关于您正在使用C ++ 17的事实,您可以使用constexpr if
生活并处理各种情况。 我猜是这样的:
template <typename U>
explicit rtvec(const rtvec<U>& in) : d(in.d), e(new T[in.d]) {
for (i32 i = 0; i < d; ++i){
if constexpr (std::is_pointer<T>::value &&
std::is_pointer<U>::value) {
// ...
} else if constexpr (!std::is_pointer<T>::value &&
std::is_pointer<U>::value) {
// ...
} else {
// rest of possible combinations
}
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.