在标准库中汇总可初始化性的类型特征? [英] Type trait for aggregate initializability in the standard library?

查看:73
本文介绍了在标准库中汇总可初始化性的类型特征?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

C ++标准库具有 std :: is_constructible< Class,T...> 检查是否可以根据给定的类型构造一个类作为参数.

The C++ standard library has std::is_constructible<Class, T...> to check if a class can be constructed from the given types as arguments.

例如,如果我有一个类 MyClass ,它具有一个构造函数 MyClass(int,char),则 std :: is_constructible< MyClass,int,char> :: value 将为 true .

For example, if I have a class MyClass which has a constructor MyClass(int, char), then std::is_constructible<MyClass, int, char>::value will be true.

是否存在类似的标准库类型特征,以检查聚合初始化是否起作用,即 MyClass {int,char} 格式正确并返回 MyClass ?

Is there a similar standard library type trait that will check that aggregate initialization works, i.e. MyClass{int, char} is well-formed and returns a MyClass?

我的用例:

我想编写一个函数模板,该模板使用聚合初始化将 std :: tuple 转换为(通常是POD)类,并带有以下签名:

I want to write a function template that converts a std::tuple to a (usually POD) class using aggregate initialization, something with the following signature:

template <typename Class, typename... T>
inline Class to_struct(std::tuple<T...>&& tp);

为了防止用户将该函数与无效的 Class 一起使用,我可以在此函数内编写一个 static_assert ,以检查给定的 tp 参数具有可转换为 Class 成员的类型.似乎像 is_aggregate_initializable< Class,T ...> 这样的类型特征会派上用场.

In order to prevent users from using this function with an invalid Class, I could write a static_assert inside this function to check if the given tp parameter has types that are convertible to the members of Class. It seems a type trait like is_aggregate_initializable<Class, T...> would come in handy.

我可以推出自己的这种特征的实现,但仅作参考,标准库中是否存在我所忽略的特征,或者即将成为标准库的一部分?

I could roll my own implementation of this trait, but just for information, is there such a trait in the standard library that I've overlooked, or one that is soon to become part of the standard library?

推荐答案

从注释中的讨论和浏览C ++参考来看,似乎存在一个标准的库类型特征,既没有集合可初始化性,也没有列表可初始化性,至少达到C ++ 17.

From the discussion in the comments and browsing the C++ reference, it seems like there is a standard library type trait for neither aggregate initializability nor list initializability, at least up to C++17.

评论中强调,列表可初始化性一般( Class {arg1,arg2,...} )和

It was highlighted in the comments that there was a distinction between list initializability in general (Class{arg1, arg2, ...}) and aggregate initializability.

列表可初始化性(特别是直接列表可初始化性)更容易为其编写类型特征,因为该特征仅取决于特定语法的有效性.对于我测试是否可以从元组的元素构造结构的用例,直接列表可初始化性似乎更合适.

List initializability (specifically direct list initializability) is easier to write a type trait for because this trait is solely dependent on the validity of a certain syntax. For my use case of testing if a struct can be constructed from the elements of a tuple, direct list initializability seems to be more appropriate.

(使用适当的SFINAE)实现此特征的一种可能方法如下:

A possible way to implement this trait (with appropriate SFINAE) is as follows:

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 ...> 来测试直接列表的可初始化性.

Then we can test direct list initializability by doing is_direct_list_initializable_v<Class, T...>.

这也适用于移动语义和完美的转发,因为 std :: declval 遵循完美的转发规则.

This also works with move semantics and perfect forwarding, because std::declval honors perfect forwarding rules.

总体可初始化性不是那么简单,但是有一种解决方案可以覆盖大多数情况.聚合初始化要求初始化的类型是聚合(请参见有关聚合的C ++参考的说明初始化),并且具有C ++ 17特性 std:: is_aggregate 检查类型是否为聚合.

Aggregate initializability is less straightforward, but there is a solution that covers most cases. Aggregate initialization requires the type being initialized to be an aggregate (see explanation on the C++ reference on aggregate initialization), and we have a C++17 trait std::is_aggregate that checks if a type is an aggregate.

但是,这并不意味着仅仅因为类型是一个聚合,通常的直接列表初始化就无效了.仍然允许与构造函数匹配的常规列表初始化.例如,以下编译:

However, it doesn't mean that just because the type is an aggregate the usual direct list initialization would be invalid. Normal list initialization that matches constructors is still allowed. For example, the following compiles:

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)>> .

To disallow this kind of list initialization, we can utilize the fact that aggregates cannot have custom (i.e. user-provided) constructors, so non-aggregate initialization must have only one parameter and Class{arg} will satisfy std::is_same_v<Class, std::decay_t<decltype(arg)>>.

幸运的是,我们可以t的成员变量与其封闭类具有相同的类型,因此以下内容无效:

Luckily, we can't have a member variable of the same type as its enclosing class, so the following is invalid:

struct point {
    point x;
};

需要注意的是:允许对同一对象的引用类型是因为成员引用可以是不完整的类型(GCC,Clang和MSVC都接受此而没有任何警告):

There is a caveat to this: reference types to the same object are allowed because member references can be incomplete types (GCC, Clang, and MSVC all accepts this without any warnings):

struct point {
    point& x;
};

此代码在标准上有效.我没有解决方案来检测这种情况并确定 point 是否可以通过 point& 类型的对象进行聚合初始化.

While unusual, this code is valid by the standard. I have no solution to detect this case and determine that point is aggregate initializable with an object of type point&.

忽略上面的警告(很少需要使用这种类型),我们可以设计一个可行的解决方案:

Ignoring the caveat above (it is rare to need to use such a type), we can devise a solution that will work:

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;

它看起来不太好,但是可以按预期运行.

It doesn't look very nice, but does function as expected.

这篇关于在标准库中汇总可初始化性的类型特征?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆