std::declare_if 或其他在编译时丢弃成员声明的假设方法
std::declare_if or other hypothetical ways for discarding member declarations at compile time
SFINAE 对丢弃函数体很有用,但为什么不能用来丢弃成员变量呢?
是否计划在某个时候将此类功能添加到现代 C++ 中?我尝试使用 std::enable_if
、std::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 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;
}
SFINAE 对丢弃函数体很有用,但为什么不能用来丢弃成员变量呢?
是否计划在某个时候将此类功能添加到现代 C++ 中?我尝试使用 std::enable_if
、std::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 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;
}