You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

空std::initializer_list初始化与默认构造行为不一致问题求解

问题:Wrap类初始化行为不一致的修复方案

我定义了一个包含std::vector的Wrap类,支持默认构造(生成空vector)和通过花括号初始化列表传值构造。期望两种初始化形式行为一致:传入空列表时也得到空vector,但实际用{{}}初始化时,会构造出含一个默认元素的vector。

示例代码:

#include <iostream>
#include <vector>

struct Wrap {
    std::vector<double> v;
    Wrap() = default;
    Wrap(std::initializer_list<double> const &l) : v(l) {}
};

int main() {
    Wrap v0 = {};
    std::cout << v0.v.size() << "; expected 0\n";
    Wrap v1 = {{}};
    // expecting 0, getting 1
    std::cout << v1.v.size() << "; expected 0\n";
}

已知这是std::initializer_list的预期行为,现寻求类重构方案满足:

  • 默认构造生成空vector;
  • 传入空花括号初始化列表时也生成空vector。

注:考虑过用T const (&)[N]的模板构造,但希望保留std::initializer_list的非窄化转换特性。

补充示例与困惑

以下代码展示了花括号初始化规则带来的矛盾:

#include <iostream>
#include <vector>

struct Wrap {
    std::vector<double> v;
    Wrap() { std::cout << "default constructor\n"; }
    Wrap(std::initializer_list<double> const &l) : v(l) {
        std::cout << "IL constructor\n";
    }
};

int main() {
    {
        Wrap v = {1, 2};
        std::cout << v.v.size() << "; expected 2\n";
    }
    {
        Wrap v = {{1, 2}};
        std::cout << v.v.size() << "; expected 2, braces elision?\n";
    }
    {
        Wrap v = {1};
        std::cout << v.v.size() << "; expected 1\n";
    }
    {
        Wrap v = {{1}};
        std::cout << v.v.size() << "; expected 1, braces elision?\n";
    }
    {
        Wrap v = {};
        std::cout << v.v.size()
                  << "; expected 0, default ctor has precedence other IL one\n";
    }
    {
        Wrap v = {{}};
        std::cout << v.v.size() << "; expected 0, braces elision?\n";
    }
}

程序输出:

Program returned: 0
IL constructor
2; expected 2
IL constructor
2; expected 2, braces elision?
IL constructor
1; expected 1
IL constructor
1; expected 1, braces elision?
default constructor
0; expected 0, default ctor has precedence other IL one
IL constructor
1; expected 0, braces elision?

解决方案

通过添加一个接受嵌套初始化列表的构造函数,利用重载解析优先级区分{{}}和普通初始化列表:

#include <iostream>
#include <vector>

struct Wrap {
    std::vector<double> v;
    Wrap() = default;
    Wrap(std::initializer_list<double> const &l) : v(l) {}
    
    // 匹配嵌套空列表{{}},初始化空vector
    Wrap(std::initializer_list<std::initializer_list<double>> const &l) : v() {}
};

int main() {
    Wrap v0 = {};          // 默认构造,size 0
    std::cout << v0.v.size() << "; expected 0\n";
    
    Wrap v1 = {{}};        // 匹配嵌套初始化列表构造,size 0
    std::cout << v1.v.size() << "; expected 0\n";
    
    Wrap v2 = {1, 2};      // 普通初始化列表,size 2
    std::cout << v2.v.size() << "; expected 2\n";
    
    Wrap v3 = {{1, 2}};    // 花括号折叠,匹配普通初始化列表,size 2
    std::cout << v3.v.size() << "; expected 2\n";
}

原理说明

  • 当使用{{}}初始化时,编译器会优先匹配接受std::initializer_list<std::initializer_list<double>>的构造函数,而非将其解析为含一个默认double元素的std::initializer_list<double>;
  • 对于非空的嵌套列表(如{{1,2}}),编译器会进行花括号折叠,优先匹配更直接的std::initializer_list<double>构造函数,保留原有初始化行为;
  • 保留了std::initializer_list的非窄化转换特性,同时解决了空嵌套列表的初始化问题。

内容的提问来源于stack exchange,提问作者Oersted

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 06:14:58