在C ++ 11中,我可以为非聚合类型实现像构造函数一样的聚合类型初始化吗?

时间:2019-07-29 10:47:56

标签: c++ c++11 templates

说,我有一个一维数组,封装在名为stA的类中

class stA
{
public:
    template<typename ... T>
    stA(T ... t):
        data_{t...}
    {}

private:
    int data_[2];
};

通过利用可变模板,我可以成功实现这一想法。

stA a = {1, 2};

但是,当我尝试将此技巧引入名为stB的二维类时,

class stB
{
public:
    template<typename ... T>
    stB(T ... t):
        data_{t...}
    {}

private:
    stA data_[2];
};

把戏失败了。

stB b = {{1, 2}, {1, 2}};
  

错误:无法从括号括起来的内容中转换'{{1,2},{1,2}}'   初始化程序列表为“ stB”        stB b = {{1,2},{1,2}};

这在开始时让我很困惑。

因为如果stB::Ctor中的 template-argument-deduction 产生T = const stA &,那么Ctor将会变成这样,

stB(const stA & a, const stA & b):
        data_{a, b}
    {}

当然还有stB b = {{1, 2}, {1, 2}};可以幸免,但会失去论点的灵活性。

完成搜索后,我终于意识到这可能违反了模板参数推导

  

非推论上下文

     

6)参数P,其A是一个括号初始化列表,但P不是   std :: initializer_list,一个引用(可能是cv限定),或   对数组的引用:

但是我还在徘徊 还有没有办法使这个想法发生以及如何发生?

1 个答案:

答案 0 :(得分:4)

问题在于{1, 2}不是表达式,只能推导表达式。

选项1:使用聚合初始化。将data_成员设为公开,并且聚合初始化使您可以初始化元素。缺点:控制力很小。

选项2:使用initializer_list。如:

class stA {
public:
    stA(std::initializer_list<int> init):
        data_{init.begin()[0], init.begin()[1]}
    {
        assert(init.size() == 2);
    }

private:
    int data_[2];
};

class stB {
public:
    stB(std::initializer_list<std::initializer_list<int>> init):
        data_{init.begin()[0], init.begin()[1]}
    {
        assert(init.size() == 2);
    }

private:
    stA data_[2];
};

缺点:大小信息在编译时丢失。

选项3:将聚合类型用作构造函数参数。

class stA {
public:
    stA(const int (&arr)[2]):
        data_{arr[0], arr[1]}
    {
    }

private:
    int data_[2];
};

class stB {
public:
    stB(const int (&arr)[2][2]):
        data_{arr[0], arr[1]}
    {
    }

private:
    stA data_[2];
};

(这需要一对额外的花括号,例如stB b = {{{1, 2}, {3, 4}}};。)