std::declare_if 或其他在编译时丢弃成员声明的假设方法

std::declare_if or other hypothetical ways for discarding member declarations at compile time

SFINAE 对丢弃函数体很有用,但为什么不能用来丢弃成员变量呢?

是否计划在某个时候将此类功能添加到现代 C++ 中?我尝试使用 std::enable_ifstd::conditional(如果允许它具有零大小的类型,这会起作用,但可能会破坏其他所有内容)。

我希望能够使用假设的 SFINAE 模式生成别名,例如:

template<class T, SIZE> 
struct Vector {
    union {
        T mArray[SIZE] = {};
        struct {
            std::declare_if<SIZE >= 1, T>::type x;
            std::declare_if<SIZE >= 2, T>::type y;
            std::declare_if<SIZE >= 3, T>::type z;
        };
    };
};

除了缺少编译器支持外,我看不出有任何充分的理由说明目前不存在这个问题? 如果您对优雅的变通方法或解决方案有任何想法,而无需为联合增加额外的大小,或编写样板代码(例如基数),然后进行部分专门化的派生。 我很想知道。

attribute [[no_unique_address]] 的帮助下,您几乎可以实现自己想要的目标, 其中:

indicates that this data member need not have an address distinct from all other non-static data members of its class. This means that if the member has an empty type (e.g. stateless Allocator), the compiler may optimise it to occupy no space.

应用于您的用例:

#include <type_traits>

template <typename T, int SIZE>
struct Vector
{
  T x;
  [[no_unique_address]] std::conditional_t<(SIZE > 1), T, decltype([]{})> y;
  [[no_unique_address]] std::conditional_t<(SIZE > 2), T, decltype([]{})> z;
};

int main()
{
  static_assert(sizeof(Vector<double, 1>) == 1 * sizeof(double));
  static_assert(sizeof(Vector<double, 2>) == 2 * sizeof(double));
  static_assert(sizeof(Vector<double, 3>) == 3 * sizeof(double));
}

这里我用decltype([]{})作为一个空类型,产生不同的类型,这样他们就可以共享同一个地址。

现在不可能,但您可以编写一个接受整数值的模板化 get() 函数。另外,如果您使用的是 C++ 17,您也可以使用结构化绑定。

#include <tuple>
#include <iostream>

// not elegant way of naming as enum will polute the whole namespace where it is defined
enum Idx {
    X = 0,
    Y = 1,
    Z = 2,
    W = 3,
    R = 0,
    G = 1,
    B = 2,
    A = 3
};

template <typename T, std::size_t SIZE>
struct Vector
{
    template<std::size_t Index>
    T& get() {
        static_assert(Index < SIZE, "Invalid Index");
        return data[Index];
    }

    template<std::size_t Index>
    const T& get() const noexcept {
        static_assert(Index < SIZE, "Invalid Index");
        return data[Index];
    }

    T data[SIZE];
};

//only needed if structured binding is required
namespace std {
template<typename T, size_t SIZE>
struct tuple_size<Vector<T, SIZE>> {
    constexpr static size_t value = SIZE;
};

template<typename T, size_t I, size_t SIZE>
struct tuple_element<I, Vector<T, SIZE>> {
    using type = T;
};
}

int main()
{
  Vector<int, 2> value = {0, 1};
  std::cout << "x = " << value.get<X>() << ": y = " << value.get<Y>() << '\n';

  // structured binding, available only in C++17
  auto& [x, y]  = value;
  std::cout << "x = " << x << ": y = " << y << '\n';

  // will generate a compiler error
  //auto& [x1, y1, z1] = value;

  // will invoke the static assert
  //auto z = value.get<Z>();
  return 0;
}