从评论中的讨论和浏览 C++ 参考来看,似乎没有聚合可初始化性和列表可初始化性的标准库类型特征,至少达到 C++17。
评论中强调,两者之间存在区别列表可初始化性 http://en.cppreference.com/w/cpp/language/list_initialization一般来说 (Class{arg1, arg2, ...}
) and 聚合可初始化性 http://en.cppreference.com/w/cpp/language/aggregate_initialization.
列表可初始化性(具体来说direct列表可初始化性)更容易为其编写类型特征,因为该特征仅依赖于某种语法的有效性。对于我测试是否可以从元组的元素构造结构的用例,直接列表可初始化性似乎更合适。
实现此特征的可能方法(使用适当的 SFINAE)如下:
namespace detail {
template <typename Struct, typename = void, typename... T>
struct is_direct_list_initializable_impl : std::false_type {};
template <typename Struct, typename... T>
struct is_direct_list_initializable_impl<Struct, std::void_t<decltype(Struct{ std::declval<T>()... })>, T...> : std::true_type {};
}
template <typename Struct, typename... T>
using is_direct_list_initializable = detail::is_direct_list_initializable_impl<Struct, void, T...>;
template<typename Struct, typename... T>
constexpr bool is_direct_list_initializable_v = is_direct_list_initializable<Struct, T...>::value;
然后我们可以通过执行以下操作来测试直接列表的可初始化性is_direct_list_initializable_v<Class, T...>
.
这也适用于移动语义和完美转发,因为std::declval
遵守完善的转发规则。
聚合可初始化性不太简单,但有一个解决方案可以涵盖大多数情况。聚合初始化要求被初始化的类型是聚合(请参阅关于有关聚合初始化的 C++ 参考 http://en.cppreference.com/w/cpp/language/aggregate_initialization),我们有一个 C++17 特征std::is_aggregate http://en.cppreference.com/w/cpp/types/is_aggregate检查类型是否是聚合。
但是,这并不意味着仅仅因为类型是聚合,通常的直接列表初始化就会无效。仍然允许与构造函数匹配的正常列表初始化。例如,以下编译:
struct point {
int x,y;
};
int main() {
point e1{8}; // aggregate initialization :)
point e2{e1}; // this is not aggregate initialization!
}
为了禁止这种列表初始化,我们可以利用聚合不能具有自定义(即用户提供的)构造函数这一事实,因此非聚合初始化必须只有一个参数,并且Class{arg}
会满足std::is_same_v<Class, std::decay_t<decltype(arg)>>
.
幸运的是,我们不能拥有与其封闭类相同类型的成员变量 https://stackoverflow.com/questions/4941629/why-cant-we-declare-object-of-a-class-inside-the-same-class,因此以下内容无效:
struct point {
point x;
};
对此有一个警告:允许对同一对象的引用类型,因为成员引用可以是不完整的类型(GCC、Clang 和 MSVC 都接受这一点,没有任何警告):
struct point {
point& x;
};
虽然不寻常,该代码根据标准有效 https://stackoverflow.com/questions/22098834/class-that-holds-a-reference-to-itself。我没有解决方案来检测这种情况并确定point
可以使用类型的对象进行聚合初始化point&
.
忽略上面的警告(很少需要使用这种类型),我们可以设计一个可行的解决方案:
template <typename Struct, typename... T>
using is_aggregate_initializable = std::conjunction<std::is_aggregate<Struct>, is_direct_list_initializable<Struct, T...>, std::negation<std::conjunction<std::bool_constant<sizeof...(T) == 1>, std::is_same<std::decay_t<std::tuple_element_t<0, std::tuple<T...>>>, Struct>>>>;
template<typename Struct, typename... T>
constexpr bool is_aggregate_initializable_v = is_aggregate_initializable<Struct, T...>::value;
它看起来不太好,但功能符合预期。