是的,有限制。你可以在mattkretz/virtools 找到我的实现。它需要 C++20,因为它使用概念。原则上,您可以使用enable_if 重写它,从而使其适用于 C++17。 Live example.
这里的主要思想是将可检查的类型集限制为在单个基类中或仅在派生类中具有非静态数据成员的聚合(但仍允许空基类)。这与结构化绑定所施加的限制相同。然后你知道一个类型 T 是否有例如如果 T{anything_but_base_of<T>(), anything_but_base_of<T>(), anything_but_base_of<T>()} 是一个有效的表达式(即没有替换失败),则 3 个(或更多)成员。其中anything_but_base_of 是:
template <class Struct> struct anything_but_base_of {
template <class T>
requires(!std::is_base_of_v<T, Struct>)
operator T();
};
由于聚合初始化允许指定的初始化器数量少于聚合的成员数,因此必须从上限开始进行测试,然后向下递归到 0,直到找到可能的聚合初始化。我的实现使用的解构测试实际上不是 SFINAE 条件,而是产生了一个硬错误。因此,您也可以删除该代码,进行实现:
namespace detail {
template <class Struct> struct any_empty_base_of {
template <class T>
requires(std::is_base_of_v<T, Struct> && std::is_empty_v<T>)
operator T();
};
template <class T, size_t... Indexes>
concept brace_constructible =
requires { T{((void)Indexes, anything_but_base_of<T>())...}; }
|| requires { T{any_empty_base_of<T>(), ((void)Indexes, anything_but_base_of<T>())...}; }
|| requires { T{any_empty_base_of<T>(), any_empty_base_of<T>(), ((void)Indexes, anything_but_base_of<T>())...}; }
|| requires { T{any_empty_base_of<T>(), any_empty_base_of<T>(), any_empty_base_of<T>(), ((void)Indexes, anything_but_base_of<T>())...}; };
template <class T, size_t... Indexes>
requires brace_constructible<T, Indexes...>
constexpr size_t struct_size(std::index_sequence<Indexes...>)
{
return sizeof...(Indexes);
}
template <class T>
requires requires { T{}; }
constexpr size_t struct_size(std::index_sequence<>)
{
static_assert(std::is_empty_v<T>,
"Increase MaxSize on your struct_size call. (Or you found a bug)");
return 0;
}
template <class T, size_t I0, size_t... Indexes>
requires(!brace_constructible<T, I0, Indexes...>)
constexpr size_t struct_size(std::index_sequence<I0, Indexes...>)
{
// recurse with one less initializer
return struct_size<T>(std::index_sequence<Indexes...>());
}
} // namespace detail
最后,我们需要一个合理的上限作为起点。正确的上限是sizeof(T) * CHAR_BIT,对于每个非静态数据成员占用一个位并且整个结构不包含填充的位域的情况。考虑到使用正确上限的编译时间成本,我选择了一个更明智的启发式方法,即 sizeof(T):
template <typename T, size_t MaxSize = sizeof(T)>
constexpr inline std::size_t struct_size =
detail::struct_size<T>(std::make_index_sequence<MaxSize>());