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

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

本文关键字:成员 声明 方法 假设 eclare if std 其他 编译      更新时间:2023-10-16

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;
        };
    };
};

除了缺乏编译器支持之外,我认为目前不存在任何充分的理由?如果您对优雅的解决方法或解决方案有任何想法,而无需向联合添加额外的大小,或编写样板代码(例如基本代码(,然后进行部分专门的派生。我很想知道。

借助

c++20 属性 [[no_unique_address]] ,哪:

指示此数据成员不需要具有与其类的所有其他非静态数据成员不同的地址。这意味着,如果成员具有空类型(例如无状态分配器(,编译器可以对其进行优化以不占用空间。

应用于您的用例:

#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;
}