如何在满足特定条件时阻止C++模板类方法的实例化?

pmj*_*bin 7 c++ template-specialization

我目前正在编写一个通用的矢量模板类(几何实体,而不是容器),带有以下签名......


template< typename T, unsigned N >
class vector
{...}
Run Code Online (Sandbox Code Playgroud)

...其中T是算术类型,N是维度.我想将交叉乘积定义为运算符的重载^(位于类定义内)并仅在N == 3时启用它.我现在拥有的是:


typename boost::lazy_enable_if_c< (N == 3), vector >::type
inline operator ^(const vector &rhs) const
{
    vector ret;
    ret(0) = val_[1] * rhs(2) - val_[2] * rhs(1);
    ret(1) = val_[2] * rhs(0) - val_[0] * rhs(2);
    ret(2) = val_[0] * rhs(1) - val_[1] * rhs(0);
    return ret;
}
Run Code Online (Sandbox Code Playgroud)

不幸的是,使用N!= 3实例化此模板,即使未引用operator ^,也会产生以下错误:


error: no type named ‘type’ in ‘struct boost::lazy_enable_if_c < false, flare::math::vector < flare::math::fixed < short int, 8u >, 2u > >’
Run Code Online (Sandbox Code Playgroud)

我究竟做错了什么?在这种情况下是否有boost :: enable_if的替代方案?

非常感谢你.

j_r*_*ker 5

根据该文档,错误消息的最接近原因是:“的第二个参数lazy_enable_if必须是定义type第一个参数(条件)为true时命名的嵌套类型的类类型。” 这里显然不满足要求(除非您的vector类型恰好包含typedef something type;)。

您不需要lazy_...这里。根据文档,只有在第二个arg可能未定义的情况下才需要(例如,如果第二个arg为typename foo<T>::bar,并且bar未为所有类型定义类型T)。 vector(此处表示vector<T, N>)将始终被定义。

所以,一定要试试除暴安良lazy_,或者创建一个什么都不做traits类template <typename T> struct nop { typedef T type; };和替换第二对Arg的lazy_enable_if_cnop<vector>。但是我的猜测是您至少已经尝试过前者。:)

现在我明白为什么这行不通了。 根据标准14.7.1 / 1:

除非已经明确实例化了类模板专业化(14.7.2)或明确了专门化(14.7.3),否则当在需要完全定义的对象类型的上下文中引用该专业化或完整性时,将隐式实例化该类模板专业化类的类型会影响程序的语义。类模板专业化的隐式实例化导致类成员函数,成员类,静态数据成员和成员模板的声明而不是定义或默认参数的隐式实例化;

因此,导致类实例化的任何事情都将尝试实例化所有方法的声明,当时将失败N != 3。因此,您似乎需要使用一种始终存在的方法来代替功能模板。不用担心,任何不错的编译器仍然可以通过以下方式进行内联:

template< typename T, unsigned N > class vector;   // Fwd decl.

template< typename T, unsigned N >
inline boost::enable_if_c< (N == 3), vector<T, N> >::type
magic(const vector<T, N>& lhs, const vector<T, N>& rhs) {
    /* Do the calculation as before... */
    return ret;
}

template< typename T, unsigned N >
class vector {
    ...
    inline vector operator ^(const vector &rhs) const {
        return magic(*this, rhs);
    }
};
Run Code Online (Sandbox Code Playgroud)

这将起作用,因为除非实际调用成员函数定义(或调用它们的地址等),否则不会实例化它们。