如何创建可变大小的c++数组与命名的公共成员,即x, y, z,…

How to create variable sized C++ array with named public members i.e. x, y, z, ...?

本文关键字:成员 数组 创建 何创建 c++      更新时间:2023-10-16

我正在寻找一种方法来创建一个通用的类似std的std::array< T, N >,它已命名的成员元素,即x, y, z,…

是否有一种方法可以使用可变模板来构造这样的对象,以便通过模板形参有条件地定义成员?

对象通过operator[]具有类似数组的访问是绝对重要的,而且它的内存占用不会因为额外的引用或指针而膨胀。

我考虑过模仿TR1的特征,但我真的不知道如何开始。

没有什么好实现的:

#include <cstddef>
template <std::size_t Extent>
struct Tuple;
// You cant not use a template for declaring member names.
// Hence a set of pre-declared tuples.
template <> struct Tuple<1> {
    double x;
    double operator [] (std::size_t idx) const { return x; }
};
template <> struct Tuple<2> {
    double x;
    double y;
    double operator [] (std::size_t idx) const { return (idx < 1) ? x : y; }
};
template <> struct Tuple<3> {
    double x;
    double y;
    double z;
    double operator [] (std::size_t idx) const {
        return (idx < 1)
            ? x
            : (idx < 2)
                ? y
                : z;
    }
};
// However, you pay the price of conditional evaluation for accessing
// the members via operator [].
// An alternative is accessing the values through a std::array and member functions:
template <> struct Tuple<1> {
    std::array<double, 1> data;
    double x() const { return data[0]; }
    double operator [] (std::size_t) const { return data[0]; }
};
template <> struct Tuple<2> {
    std::array<double, 2> data;
    double x() const { return data[0]; }
    double y() const { return data[1]; }
    double operator [] (std::size_t idx) const { return data[idx]; }
};
template <> struct Tuple<3> {
    std::array<double, 3> data;
    double x() const { return data[0]; }
    double y() const { return data[1]; }
    double z() const { return data[2]; }
    double operator [] (std::size_t idx) const { return data[idx]; }
};
// However, now you have to write tuple.x(), which is annoying
// in mathematical equations.

对于完整性:另一个选项是通过联合进行类型双关语。但这是向未定义行为的阴暗面的移动(参见:对c++中类型双关语的看法?)。

我要在玩了一段时间后回答我自己的问题。我想我已经找到了我要找的东西,虽然这只是一个粗略的草图。我将把这个贴出来,以防其他人对类似的问题感到好奇。

定义: Tuple.hpp

#include <cassert>
#include <iostream>
namespace details
{
    template<bool>
    struct rule_is_greater_than_4;
    template<>
    struct rule_is_greater_than_4<true> {};
    template<>
    struct rule_is_greater_than_4<false> {};
    template<class T, size_t N, size_t M>
    class inner_storage : rule_is_greater_than_4< ( M > 4 )>
    {
    public:
        T x, y, z, w;
    private:
        T more_data[ N - 4 ];
    };
    template<class T, size_t N>
    class inner_storage<T, 2, N>
    {
    public:
        T x, y;
    };
    template<class T, size_t N>
    class inner_storage<T, 3, N>
    {
    public:
        T x, y, z;
    };
    template<class T, size_t N>
    class inner_storage<T, 4, N>
    {
    public:
        T x, y, z, w;
    };
}
template<class T, size_t N>
class Tuple : public details::inner_storage<T, N, N>
{
public:
    static_assert( N > 1, "Size of 'n-tuple' must be > 1." );

    // -- Constructors --
    Tuple();
    Tuple( T k );
    Tuple( T x, T y );
    Tuple( T x, T y, T z );
    Tuple( T x, T y, T z, T w );
    // -- Access operators --
    const size_t size();
    T &operator[]( const size_t i );
    T const &operator[]( const size_t i ) const;
    // -- Unary arithmetic operators --
    Tuple<T, N> &operator=( Tuple<T, N> const &t );
    friend std::ostream &operator<<( std::ostream &os, Tuple<T, N> &t );
};
// -- Unary operators --
template<class T, size_t N>
Tuple<T, N> operator+( Tuple<T, N> const &t );
template<class T, size_t N>
Tuple<T, N> operator-( Tuple<T, N> const &t );
// -- Binary arithmetic operators --
template<class T, size_t N>
Tuple<T, N> operator+( Tuple<T, N> const &t1, Tuple<T, N> const &t2 );
template<class T, size_t N>
Tuple<T, N> operator-( Tuple<T, N> const &t1, Tuple<T, N> const &t2 );
template<class T, size_t N>
Tuple<T, N> operator*( T const &s, Tuple<T, N> const &t2 );
template<class T, size_t N>
Tuple<T, N> operator*( Tuple<T, N> const &t1, T const &s );
template<class T, size_t N>
Tuple<T, N> operator/( Tuple<T, N> &t1, T const &s );
// -- Boolean operators --
template<class T, size_t N>
bool operator==( Tuple<T, N> const &t1, Tuple<T, N> const &t2 );
template<class T, size_t N>
bool operator!=( Tuple<T, N> const &t1, Tuple<T, N> const &t2 );
// -- Stream operator --
template<class T, size_t N>
inline std::ostream &operator<<( std::ostream &os, Tuple<T, N> const &t );
#include "Tuple.inl"

实现: Tuple.inl

// -- Constructors --
template <class T, size_t N>
Tuple<T, N>::Tuple()
{
}
template <class T, size_t N>
Tuple<T, N>::Tuple( T k )
{
    for( size_t i = 0; i < N; i++ )
    {
        operator[]( i ) = k;
    }
}
template <class T, size_t N>
Tuple<T, N>::Tuple( T x, T y )
{
    static_assert( N == 2, "This constructor is resererved for 2-tuples." );
    this->x = x;
    this->y = y;
}
template <class T, size_t N>
Tuple<T, N>::Tuple( T x, T y, T z )
{
    static_assert( N == 3, "This constructor is resererved for 3-tuples." );
    this->x = x;
    this->y = y;
    this->z = z;
}
template <class T, size_t N>
Tuple<T, N>::Tuple( T x, T y, T z, T w )
{
    static_assert( N == 4, "This constructor is resererved for 4-tuples." );
    this->x = x;
    this->y = y;
    this->z = z;
    this->w = w;
}
// -- Access operators --
template <class T, size_t N>
const size_t Tuple<T, N>::size()
{
    return N;
}
template <class T, size_t N>
T &Tuple<T, N>::operator[]( const size_t i )
{
    assert( i < N );
    return ( &( this->x ) )[ i ];
}
template <class T, size_t N>
T const &Tuple<T, N>::operator[]( const size_t i ) const
{
    assert( i < N );
    return ( &( this->x ) )[ i ];
}
// -- Unary arithmetic operators --
template<class T, size_t N>
Tuple<T, N> &Tuple<T, N>::operator=( Tuple<T, N> const &t )
{
    for( size_t i = 0; i < size(); i++ )
    {
        this->operator[]( i ) = t[ i ];
    }
    return *this;
}
// -- Unary operators --
template<class T, size_t N>
Tuple<T, N> operator+( Tuple<T, N> const &t )
{
    return t;
}
template<class T, size_t N>
Tuple<T, N> operator-( Tuple<T, N> const &t )
{
    return t * T( 0.0f );
}
// -- Binary operators --
template<class T, size_t N>
Tuple<T, N> operator+( Tuple<T, N> const &t1, Tuple<T, N> const &t2 )
{
    Tuple<T, N> sum;
    for( size_t i = 0; i < N; i++ )
    {
        sum[ i ] = t1[ i ] + t2[ i ];
    }
    return sum;
}
template<class T, size_t N>
Tuple<T, N> operator-( Tuple<T, N> const &t1, Tuple<T, N> const &t2 )
{
    Tuple<T, N> difference;
    for( size_t i = 0; i < N; i++ )
    {
        difference[ i ] = t1[ i ] - t2[ i ];
    }
    return difference;
}
template<class T, size_t N>
Tuple<T, N> operator*( Tuple<T, N> const &t, T const &s )
{
    Tuple<T, N> product;
    for( size_t i = 0; i < N; i++ )
    {
        product[ i ] = t[ i ] * s;
    }
    return product;
}
template<class T, size_t N>
Tuple<T, N> operator*( T const &s, Tuple<T, N> const &t )
{
    Tuple<T, N> product;
    for( size_t i = 0; i < N; i++ )
    {
        product[ i ] = s * t[ i ];
    }
    return product;
}
template<class T, size_t N>
Tuple<T, N> operator/( Tuple<T, N> const &t, T const &s )
{
    assert( s != T( 0.0f ) );
    Tuple<T, N> quotient;
    T denom = T( 1.0f ) / s;
    for( size_t i = 0; i < N; i++ )
    {
        quotient[ i ] = t[ i ] * denom;
    }
    return quotient;
}
// -- Boolean operators --
template<class T, size_t N>
bool operator==( Tuple<T, N> const &t1, Tuple<T, N> const &t2 )
{
    bool equal = true;
    for( size_t i = 0; i < N; i++ )
    {
        equal = ( t1[ i ] == t2[ i ] ) ? equal : false;
    }
    return equal;
}
template<class T, size_t N>
bool operator!=( Tuple<T, N> const &t1, Tuple<T, N> const &t2 )
{
    return !( t1 == t2 );
}
// -- Stream operator --
template <class T, size_t N>
inline std::ostream &operator<<( std::ostream &os, Tuple<T, N> const &t )
{
    os << "( ";
    for( size_t i = 0; i < t.size(); i++ )
    {
        os << t[ i ];
        if( i < t.size() - 1 )
        {
            os << ", ";
        }
    }
    os << " )";
    return os;
}

我不确定是否涵盖了所有的操作符重载,但我想简短一点。我也不满意构造函数的创建方式(也许可变模板可以拯救)。至少它给了我以下期望的功能:

typedef Tuple<float, 2> Vec2;
typedef Tuple<float, 4> Vec4;
typedef Tuple<float, 10> Vec10;
Vec2 a( 1.0 );
a.x = 3.0f;
a.y = -3.0f;
assert( a[ 1 ] == -3.0f ); // this works
// a.z = 1.0f; ------> compiler error
Vec10 b;
b.x = 0.0f; b.y = 1.0f; b.z = 2.0f; b.w = 3.0f;
b[ 5 ] = 12.0f;
// etc...

假设您想要存储int

的类型
using mytype = int;
struct mytuple_ {
  mytype x;
  mytype y;
  mytype z;
} tup;

这符合您的标准,不增加任何额外的内存使用,只是在内存中布局数据。

设置x=5

tup.x = 5;