根据模板参数条件创建成员别名 C++17

Create member alias based on a template parameter condition C++17

所以,我试图简化我的通用 classes 的使用,并想到了以下想法:

给出以下结构:

template <size_t size>
struct Vector {
    std::array<float, size> data;

    float& x = data[0];
    float& y = data[1]; // Declare y only if size is > 0
    float& z = data[2]; // Declare z only if size is > 1
    float& w = data[3]; // Declare w only if size is > 2
};

显然,如果我尝试 运行 这样的程序,数组将抛出超出范围的异常。

现在有没有一种方法可以仅在给定条件(编译时已知)的情况下声明这些别名?

我想到了 std::enable_if 的方式:

template <size_t size>
struct Vector {
    std::array<float, size> data;

    float& x = data[0];
    declare_if<(size > 0), float&> y = data[1];
    declare_if<(size > 1), float&> z = data[2];
    declare_if<(size > 2), float&> w = data[3];
};

此外,我 不希望 让 class 从另一个派生,或者完全专门化 class。

最有可能的是求助于专业化:

template <size_t size>
class Vector {
    std::array<float, size> data;

    float& x = data[0];
    float& y = data[1]; // Declare y only if size is > 1
    float& z = data[2]; // Declare z only if size is > 2
    float& w = data[3]; // Declare w only if size is > 3
};
template<> class Vector<0> {
    std::array<float, 0> data;
};
template<> class Vector<1> {
    std::array<float, 1> data;
    float &x = data[0];
};

(请注意,我已将注释中的大小限制更改为不超过数组的边界。)

此外,如果您不喜欢完全专业化的想法,那么无论如何您都需要在通用案例中引入这些成员,但它们的默认绑定可以更改:

template <size_t size>
class Vector {
    std::array<float, size> data;

    float& x = data[0];
    float& y = data[std::min(size - 1, 1)];
    float& z = data[std::min(size - 1, 2)];
    float& w = data[std::min(size - 1, 3)];
};

(如果您同意 x、y、z 和 w 指的是同一个数组元素。)

为什么不声明一个函数模板 float& get() ,你可以 std::enable_if 基于大小?

据我所知,您可以做到这一点,并且仅通过继承或专门化来保留您的语法。

如果你想避免这种情况,你需要稍微改变一下界面。您需要创建 xyzt 方法。实际方法模板:

template <size_t size>
struct Vector {
    std::array<int, size> data;

    template <std::size_t S = size, class = std::enable_if_t<(S > 0)>>
    auto x() -> int& { return data[0]; };

    template <std::size_t S = size, class = std::enable_if_t<(S > 1)>>
    auto y() -> int& { return data[1]; };

    template <std::size_t S = size, class = std::enable_if_t<(S > 2)>>
    auto z() -> int& { return data[2]; };

    template <std::size_t S = size, class = std::enable_if_t<(S > 3)>>
    auto t() -> int& { return data[3]; };  
};
Vector<2> v;

v.x();
v.y();
v.z(); // error: no matching member function for call to 'z'

我建议采用与创建引用其他成员的成员略有不同的方法(如果您希望 class 可赋值,则必须为每个成员手动实现赋值运算符)。

为什么不用一些免费的功能来为您访问?

template <std::size_t i, std::size_t size>
auto & get(Vector<size> & v) { return std::get<i>(v.data); }

template <std::size_t size>
auto & x(Vector<size> & v) { return get<0>(v.data); }

template <std::size_t size>
auto & y(Vector<size> & v) { return get<1>(v.data); }

//...

如果您尝试访问大小不足的向量中的成员,这将产生编译时错误

如果函数返回引用而不是引用 public 成员是一个选项,您可以选择:

#include <array>
#include <type_traits>

template<size_t size>
struct Vector
{
    std::array<float, size> data;

    template<size_t s = size, std::enable_if_t<s >= 1, int> = 0> float& x() { return data[0]; }
    template<size_t s = size, std::enable_if_t<s >= 2, int> = 0> float& y() { return data[1]; }
    template<size_t s = size, std::enable_if_t<s >= 3, int> = 0> float& z() { return data[2]; }
    template<size_t s = size, std::enable_if_t<s >= 4, int> = 0> float& w() { return data[3]; }
};

我同意建议使用函数而不是访问引用的其他答案。到目前为止没有提到的一个原因是,对于引用,您不能对数据强制执行常量:const 对象中的 float& 仍然是可修改的 ,即使它引用了 const 成员变量 .来自您的代码的示例:

#include <iostream>

template <size_t size>
struct Vector {

  Vector() { std::fill(data, data + 4, 0.0f); }

  float data[4];

  float& x = data[0];
  float& y = data[1]; 
  float& z = data[2]; 
  float& w = data[3]; 

  // read-write access
  float& getx() { return data[0]; }
};

int main()
{
  const Vector<4> v;

  //++v.data[0]; // compile error

  std::cout << v.x << std::endl;
  ++v.x; // modifies const through non-const ref!
  std::cout << v.x << std::endl;

  //++v.getx(); // compile error (member function enforces const)

}