如何避免为聚合初始化重新输入类型信息?

Posted

技术标签:

【中文标题】如何避免为聚合初始化重新输入类型信息?【英文标题】:How to avoid retyping type information for aggregate initialization? 【发布时间】:2019-10-07 18:07:24 【问题描述】:

我有以下例子:

#include <array>

struct A 
    const char* str;
    const char* str2;
;

template<size_t N>
struct As 
    std::array<A,N> elems_;
;


template<class... Args>
As(Args...)->As<sizeof...(Args)>; //<-- NOTE: deduction guide !


constexpr static As asA"a","b", A"1","2";//<-- 'retyping' A here


int main() 
  return as.elems_.size(); 

Link to non-working example

虽然此代码有效,但我想避免在聚合列表中“重新输入”A,但如果我将其排除在外,则演绎指南将失败:"cannot deduce template arguments for 'As'"(我想这是有道理的)。也许解决这个问题的一种方法是手写我需要的任何数量的推导指南,从那时起我可以在每个推导指南中写A类型(即:我需要的容器的每个尺寸都推导一个)。

【问题讨论】:

初始化时多加一组好吗? @Columbo 我想这可能没问题 - 无论如何如果找到使用它的解决方案可能会帮助其他用户。 ..没有类型,所以如果A被省略,Args...就无法推导出来。 【参考方案1】:

嵌套聚合初始化具有您添加嵌套/ 来匹配的或多或少令人惊讶的行为。

比如this就是你初始化std::array&lt;std::array&lt;int, 2&gt;, 2&gt;的方式:

std::array<std::array<int, 2>, 2> arr =  1, 1, 2, 2 ;

1, 1, 2, 2 做not 工作!

同样,在您的情况下,聚合初始化不需要嵌套 /,CTAD 已经完成(嗯,给出了模板参数):

constexpr static As<2> as"a","b", "1","2"; // Ok!

了解了这一点,我们就可以添加如下的推导指南:

template<class ... Ts>
As(Ts...) -> As<(sizeof...(Ts) + 1)/2>;

这会选择N 作为“参数数量的一半”。事实上,现在你可以像这样初始化:

constexpr static As as"a","b", "1","2"; // Deduces N = 2.

演示:https://godbolt.org/z/oznEwl

是的,这失去了使用/ 构造输入以提高可读性的能力,但我在这里责备聚合初始化(参见上面的嵌套数组示例)。

【讨论】:

(sizeof...(Ts)+1)/2? +1:在我的实际情况中,我不能直接使用它,因为我在 A 结构中还有一些成员,这只是有时指定的。即便如此,这也很好地回答了上述问题。我保持打开状态,看看是否有更多答案出现。 @Columbo 我想这比我一开始写的更正确,尽管我会考虑检查sizeof... 是否是偶数。取决于用例(即,如果给定的初始化程序太少,您是否真的想用 nullptr 初始化最后一个成员)哪个更好。 不过需要一些 SFINAE,因为我们期望 As&lt;4&gt; 对应 A a/*..*/; As asa, a, a, a 1, 1, 2, 2 不起作用!那是因为你还没有完全扩展它应该是什么。使用 1, 1, 2, 2 有效,因为外大括号创建数组,内外大括号为外数组创建初始化列表,内大括号是每个元素。您也可以全力以赴并使用 1, 1, 2, 2 ; 来具体化。

以上是关于如何避免为聚合初始化重新输入类型信息?的主要内容,如果未能解决你的问题,请参考以下文章

具有累积聚合样式的 HKQuantityType 月份的统计信息

使用不同的类型和消息重新引发异常,保留现有信息

极简spark教程spark聚合函数

Redis使用:聚合类型为空时,会自动被Redis删除

如何避免大型CKAssets的重新传输,跟随其他数据更新

使用空基类的聚合初始化时如何避免