c ++中的静态公共值函数

时间:2017-07-02 21:31:27

标签: c++ c++11

假设我们有Matrix4类(矩阵四乘四)。

该类的用户经常希望用单位矩阵初始化Matrix4类型的变量。

我知道有两种选择:

  1. 为该类提供更改* this值的成员函数SetAsIdentity。所以用户会做这样的事情:

    Mat4 m;
    m.SetAsIdentity();
    
  2. 提供返回静态const单位矩阵的静态成员(或非成员)函数,如下所示(请参阅更新):

    static Mat4 Mat4::Identity()
    {
        static const Mat4 m{ 1, 0, 0, 0,   0, 1, 0, 0, 
                             0, 0, 1, 0,   0, 0, 0, 1 };
        return m;
    }
    

    然后像这样使用它:

    Mat4 m = Mat4::Identity();
    
  3. 在我看来,我想使用2个以上只是因为它可以在一行代码中使用。 有没有更好的方法来初始化矩阵,但仍然使用默认默认构造函数(最初的原因是让类保持POD)?可以用constexpr实现2号吗?速差,如果有的话?

    更新

    要求:

    • 默认ctor是默认
    • 必须能够定义非默认的ctor(2实际上并不符合这个。它在msvc上编译但在gcc或clang上不编译。)

    一条评论(由juanchopanza提出)建议使用虚拟类型:

    class Mat4
    {
    public:
        struct SIdentity {};
    
        float f[16];
    
        Mat4() = default;
    
        Mat4(SIdentity) : f{ 1, 0, 0, 0 , 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1}
        {}
    };
    

    并初始化它:

    Mat4 m(Mat4::SIdentity{});
    

    似乎(?)更优雅(虽然它需要笨拙的实例化,变量会被优化掉吗?)

    UPDATE2:

    一条评论(由geza提出)建议将SIdentity ctor作为constexpr ctor。这增加了能够进行(静态)常量初始化和编译时初始化/构建的好处

2 个答案:

答案 0 :(得分:2)

您的问题的答案是没有理由使用到目前为止提供的任何技术。

  1. 不要使用构造后调用的变异函数。这是丑陋的,因为它是两行而不是一行,更糟糕的是你失去了制作值const或constexpr的机会。
  2. 不要将静态local用于可以构成constexpr的构造函数。编辑:虽然在这种情况下没有真正的性能影响(正如我之前错误地写过的),具有静态本地的函数不能是constexpr。因此,对于constexpr可构造类型来说,这仍然是一个糟糕的选择。另一种方法是使用constexpr static global。
  3. 没有任何理由可以采用hacky这样的东西来为构造函数提供标记类型。
  4. 我根本不理解与POD的冲突。将类构造为特殊值的最简单方法是简单地使静态函数按值返回(不使用静态局部)。以下似乎满足您的所有要求。

    #include <array>
    #include <type_traits>
    
    struct MatrixFour {
    
        MatrixFour() = default;
        constexpr MatrixFour(const std::array<double, 16> x) : m_data(x) {}
    
        static constexpr MatrixFour makeIdentity() {
            return MatrixFour({1,0,0,0,
                               0,1,0,0,
                               0,0,1,0,
                               0,0,0,1});
        }
    
    private:
        std::array<double, 16> m_data;
    };
    
    int main() {
        static_assert(std::is_pod<MatrixFour>::value, "");
        constexpr auto x = MatrixFour::makeIdentity();
        return 0;
    }
    

答案 1 :(得分:1)

如果您不关心默认情况下将Matrix初始化为单位矩阵,您可以简单地编写类似这样的内容(简化数据结构)。在这里,每个MatrixPOD首先都是一个单位矩阵,直到您将值更改为其他值或添加覆盖x的初始值的其他构造函数:

struct MatrixPOD {
public:
    int x[4] = { 0,0,0,1 };
    MatrixPOD() { };
};

MatrixPOD gi;  // identity matrix, even at file scope

如果您可以使用普通数据结构,您还可以提供用于初始化的宏,例如:

struct MatrixPOD2 {
public:
    int x[4];
#define IDENTITY { 0,0,0,1 }
};

MatrixPOD2 gi2 = IDENTITY;

但是,如果您希望拥有一个包含更多构造函数的类,并且如果您希望仅在明确声明时进行“预初始化”,那么您可以引入一个全局标识实例,然后使用该实例初始化其他矩阵对象。请参阅以下代码,其中私有构造函数用于初始化此全局标识实例:

struct Matrix {
public:
    int x[4];
    Matrix() = default;

    const static Matrix identity;

private:
    Matrix(bool identity) {
        if (identity)
            memcpy (x,x_identity,sizeof(x));
    };

    const static int x_identity[4];
};

const Matrix Matrix::identity(true);
const int Matrix::x_identity[4] = { 0,0,0,1 };


int main() {

    MatrixPOD p;  // always an identity matrix
    Matrix m; // not an identity matrix
    Matrix mi = Matrix::identity;  // identity matrix since explicitly assigned

    return 0;
}