【发布时间】:2019-04-07 12:30:36
【问题描述】:
我想了解在尽量减少成员函数模板专业化的冗长程度时我哪里出错了。我在相当随意地这样做时会遇到编译错误。这是有效的版本,希望能对我想要实现的目标有所启发:
#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 中也被注释掉)。我想了解是什么导致第二个代码无法编译。
我不直接专门化该类的原因是,在我的原始实现中,我有很多其他成员函数,它们在两个专门化之间是等效的,我不想重复。
【问题讨论】:
-
您是否知道,如果您将复制构造函数模板化,它就不再是复制构造函数了? eel.is/c++draft/class.copy.ctor#1
-
无论如何,我相信你可以在这里找到一些相关信息:std::enable_if to conditionally compile a member function。根据那里的答案,第三种情况下的 SFINAE 也不能正常工作。
-
实际上我认为模板应该使复制 ctor 只是一个自定义 ctor,但后来 MSVC 吐槽说我已经定义了两个复制 ctor,然后我把它弄丢了。基本上我不知道发生了什么,我通常会按照我期望直观地工作,但最后 5 次我决定让模板更多地涉及到,它以我报告的编译器中的错误结束 5 次我相信他们在某个时候修复了 MS,所以此时我只是问其他人它是否按预期工作。
-
"更准确地说,我有一组点的每个坐标的几个数组,我想使用“参考”向量来处理这些点,就好像它们是被订购的一样反过来在内存中"你知道reverse iterator的概念吗?
-
另外,请注意,与
typedef的名称可能暗示的相反,i32不能保证是 32 位整数。是的,它恰好在 MSVC 上。但是,如果您的代码的正确性确实取决于具有保证的固定位数的整数,那么为什么不直接使用<cstdint>中提供的固定宽度整数类型呢? (注意:我真的不明白为什么用于表示rtvec大小的精确位数在这里是必不可少的)
标签: c++ templates sfinae member-functions enable-if