如果向量是特定的长度,则使用C++11 std::enable_if来启用成员函数
Using C++11 std::enable_if to enable member function if vector is specific length
我正在编写一个简单的向量类,我希望拥有一些仅在特定长度的向量中可用的成员函数(例如,3元素向量的叉积)。我偶然发现了std::enable_if,它看起来可以做我想做的事情,但我似乎无法让它正常工作。
#include <iostream>
#include <type_traits>
template<typename T, unsigned int L>
class Vector
{
private:
T data[L];
public:
Vector<T,L>(void)
{
for(unsigned int i = 0; i < L; i++)
{
data[i] = 0;
}
}
T operator()(const unsigned int i) const
{
return data[i];
}
T& operator()(const unsigned int i)
{
return data[i];
}
Vector<typename std::enable_if<L==3, T>::type, L> cross(const Vector<T,L>& vec2) const
{
Vector<T,L> result;
result(0) = (*this)(1) * vec2(2) - (*this)(2) * vec2(1);
result(1) = (*this)(2) * vec2(0) - (*this)(0) * vec2(2);
result(2) = (*this)(0) * vec2(1) - (*this)(1) * vec2(0);
return result;
}
};
int main(void)
{
Vector<double,3> v1;
Vector<double,3> v2;
Vector<double,3> v3;
//Vector<double,4> v4;
v1(0) = 1;
v1(1) = 2;
v1(2) = 3;
v2(0) = 4;
v2(1) = 5;
v2(2) = 6;
v3 = v1.cross(v2);
std::cout << v3(0) << std::endl;
std::cout << v3(1) << std::endl;
std::cout << v3(2) << std::endl;
return 0;
}
上面的代码编译并正确运行,但是,如果我取消注释Vector<double,4> v4
的声明,我在编译时会得到以下错误:
vec.cpp: In instantiation of ‘class Vector<double, 4u>’:
vec.cpp:46:22: required from here
vec.cpp:29:59: error: no type named ‘type’ in ‘struct std::enable_if<false, double>’
有人能指出我哪里错了吗?
template<unsigned LL = L>
Vector<typename std::enable_if<LL==3 && L == 3, T>::type, LL>
cross(const Vector<T,LL>& vec2) const
{
Vector<T,L> result;
result(0) = (*this)(1) * vec2(2) - (*this)(2) * vec2(1);
result(1) = (*this)(2) * vec2(0) - (*this)(0) * vec2(2);
result(2) = (*this)(0) * vec2(1) - (*this)(1) * vec2(0);
return result;
}
PS。为什么这样?
变量v4
的定义导致类模板Vector
的隐式实例化,这反过来又导致类成员函数的声明的隐式初始化等(14.7.1隐式实例化[temp.inst]#1)。当然,后一种实例化会导致一个错误。
如果我们改为将成员函数更改为成员模板,根据相同的子句,此时成员模板本身被实例化,并且这个实例化看起来或多或少类似于:
template<unsigned LL = 3>
Vector<typename std::enable_if<LL==3 && 3 == 3, double>::type, LL>
cross(const Vector<double,LL>& vec2) const;
这是一个完全有效的模板声明。在这一点上,我们不(也不能)执行任何进一步的实例化。
然而,当我们试图实际调用cross
时,毫无疑问,这是"一个需要成员/函数定义存在的上下文",因此,根据(14.7.1隐式实例化[temp.inst]#2,#3),cross
模板(第二个cross
模板,作为外部类模板实例化的结果的模板)被隐式实例化,并且std::enable_if
有机会做它的工作。附带说明一下,这就是适用SFINAE原则的情况。
PPS。进一步阐述一下,虽然与OP问题没有直接联系,但仍然值得一提的是,为了处理类似的情况,并不总是需要将成员声明为模板。
在某些情况下,类模板的成员对于给定的实例化不是"有效的",但类模板仍然可以实例化,例如:
#include <type_traits>
template<typename T>
struct S
{
T x;
T foo () const { return x; }
typename std::remove_pointer<T>::type bar () const { return *x; }
};
S<int> x;
S<int *> y;
显然,在实例化S<int>
中,表达式*x
是无效的,因为x
的类型是int
。不过,这个程序是正确的。重要的一点是,在隐式实例化期间,只有成员的声明被实例化。在上述情况下,实例化S<int>
会导致实例化声明int bar() const;
,这是一个完全正确的声明。
当然,如果我们稍后尝试实例化S<int>::bar
的定义,如:
void f()
{
x.foo ();
// x.bar (); // error
y.foo ();
y.bar ();
}
我们会得到一个错误。
(这仍然遵循C++标准的上述两段)
- 使用std::multimap迭代器创建std::list
- C++中std::resize(n)和std::shrink_to_fit之间的区别
- 来自 std::list 的迭代器 .end() 按预期返回"0xcdcdcdcdcdcdcdcd"但 .begin()
- C++17复制构造函数,在std::unordereded_map上进行深度复制
- 如何导出包含具有"std::unique_ptr"值的"std::map"属性的
- 从持续时间构造std::chrono::system_clock::time_point
- std::具有相同基类的类的变体
- std::向量与传递值的动态数组
- 使用std::vector的OpenCL矩阵乘法
- std::map<struct,struct>::find 找不到匹配项,但是如果我循环通过 begin() 到 end(),我在那里看到匹配项
- std::condition_variable::wait()如何评估给定的谓词
- 如何获取std::result_of函数的返回类型
- std::原子加载和存储都需要吗
- 将对象移动到std::shared_ptr
- POCO::PostgreSQL:如何将std::vector支持添加到`Binder::bind`
- 使用一个考虑到std::map中键值的滚动或换行的键
- 如何从 std::atomic 中提取指针 T<T>?
- 为什么 std::unique 不调用 std::sort?
- 使用std::函数映射对象方法
- Linux gnu++11,在运行时获取"Enable multithreading to use std::thread: Operation not permitted"