我有一个菜单系统,我想从常量数据初始化.A MenuItem可以包含作为子菜单的向量MenuItems.但它只能达到一定程度.以下是问题的根本原因:
#include <vector>
struct S { std::vector<S> v ; } ;
S s1 = { } ;
S s2 = { { } } ;
S s3 = { { { } } } ;
Run Code Online (Sandbox Code Playgroud)
g++ -std=c++0x(版本4.4.5)应对s1和s2,但s3回来:
prog.cpp:6:22: error: template argument 1 is invalid
Run Code Online (Sandbox Code Playgroud)
(见ideone).难道我做错了什么?
#include <vector>
struct foo {
int i;
int j;
int k;
};
int main() {
std::vector<foo> v(1);
v[0] = {0, 0, 0};
return 0;
}
Run Code Online (Sandbox Code Playgroud)
使用g ++编译时,我收到以下警告:
警告:扩展初始化列表仅在-std = c ++ 0x或-std = gnu ++ 0x [默认启用]时可用
据我所知,它只是一个普通的初始化列表.结构是POD类型.
这是一个错误还是我错过了什么?
以下代码无法在GCC 4.7.2或Clang 3.2中编译:
#include <vector>
#include <functional>
int main()
{
std::vector<std::function<void()>> a;
std::vector<std::function<void()>> b{a};
}
Run Code Online (Sandbox Code Playgroud)
问题是编译器将尝试使用initializer_list创建b,显然它应该只是调用复制构造函数.然而,这似乎是期望的行为,因为标准表明initializer_list构造函数应该优先.
这段代码适用于其他std :: vector,但对于std :: function,编译器无法知道你是否需要initializer_list构造函数或其他构造函数.
它似乎没有办法解决它,如果是这种情况,那么你永远不能在模板化代码中使用统一初始化.这将是一个巨大的耻辱.
另一方面,Visual Studio(2012年11月CTP)并没有抱怨这一点.但是此时initializer_list支持不是很好,所以它可能是一个bug.
在c ++ 11的当前状态(比如gcc 4.7.2)中,我应该如何选择使用variadic-template还是std::initializer_list需要可以接受变量参数的构造函数?
编辑:在我们开始之前,这个问题不是关于正确使用std::initializer_list; 它是关于在需要方便的语法时应该传递什么.谢谢你坚持主题.
C++ 11引入std::initializer_list了定义接受braced-init-list参数的函数.
struct bar {
bar( char const * );
bar( int );
} dog( 42 );
fn foo( std::initializer_list< bar > args );
foo( { "blah", 3, dog } );
Run Code Online (Sandbox Code Playgroud)
语法很好,但由于各种问题,它很糟糕:
他们无法有意义地感动.上述功能必须dog从列表中复制; 这不能转换为移动建筑或省略.仅移动类型根本无法使用.(好吧,const_cast实际上是一个有效的解决方法.如果有一篇关于这样做的文章,我希望看到它.)
也没有constexpr语义.(这在C++ 1y即将发布.但这是一个小问题.)
const不像其他地方那样传播; 的initializer_list是从来没有const,但它的内容始终是.(因为它不拥有其内容,所以它不能对副本进行写访问,尽管将它复制到任何地方都很少是安全的.)
该initializer_list对象不拥有其存储(yikes); 它与提供存储的完全独立的裸阵列(yikes)的关系被隐含地定义(yikes)作为引用与绑定临时(四倍yikes)的关系.
我相信这些事情会在适当的时候得到解决,但是现在是否有最佳实践来获得没有硬编码的优势initializer_list?是否有关于直接依赖它的文献或分析?
显而易见的解决方案是通过值传递标准容器,例如std::vector.一旦将对象从中复制到其中initializer_list,它就会被移动构造为按值传递,然后您可以移出内容.改进是在堆栈上提供存储.一个好的图书馆也许能提供大部分的优点initializer_list,array和vector,甚至不需要使用前者.
有资源吗?
统一初始化是一个重要且有用的C++ 11特性.但是,您不能{}随处使用,因为:
std::vector<int> a(10, 0); // 10 elements of value zero
std::vector<int> b({10, 0}); // 2 elements of value 10 and 0 respectively
std::vector<int> c{10, 0}; // 2 elements of value 10 and 0 respectively
std::vector<int> d = {10, 0}; // 2 elements of value 10 and 0 respectively
auto e(0); // deduced type is int
auto f = 0; // deduced type is int
auto g{0}; // deduced type is std::initializer_list<int>
auto h = {0}; // …Run Code Online (Sandbox Code Playgroud) 初始化列表表达式非常便于初始化C++容器:
std::vector<int>({1, 2, 3})
Run Code Online (Sandbox Code Playgroud)
......但似乎一个括号内的初始化列表表达式,如{1,2,3}将只绑定到需要的功能std::initializer_list<int>-它并不似乎绑定到一个通用(转发)参考:
template <class T>
void foo(T&& v)
{
std::vector<int>(std::forward<T>(v));
}
int main()
{
foo({1, 2, 3})
}
Run Code Online (Sandbox Code Playgroud)
这输出:
test2.cpp:11:6: note: template<class U> void foo(U&&)
test2.cpp:11:6: note: template argument deduction/substitution failed:
test2.cpp:33:13: note: couldn't deduce template parameter ‘U’
Run Code Online (Sandbox Code Playgroud)
(这是GCC 4.7.2的结果.)
遗憾的是,这意味着我们无法转发初始化列表表达式.既然这样做很方便,我想问为什么这不起作用?为什么括号封闭的初始化列表表达式不能绑定到转发引用?或者这是允许的,也许我的编译器太老了?
C++中的初始化列表构造函数经常会引起麻烦; 例如
using std::vector;
using std::string;
vector<string> v{3}; // vector of three empty strings
vector<int> u{3}; // vector of one element with value 3
Run Code Online (Sandbox Code Playgroud)
(只是为了澄清,我的意思是<int>构造函数是一个初始化列表构造函数,而<string>一个不是.)
该int案例与初始化列表构造函数匹配,而string案例则不匹配.这有点难看,常常会造成麻烦.在Scott Meyers的Effective Modern C++的早期章节(第7项)中也注意到了这一点,他将其描述为标准中有些令人不快的部分,每当初始化列表构造函数可用时,编译器将跳过箍尝试匹配它,优先于每个其他构造函数.
当然,int case可以通过改变u{3}来轻松修复u(3),但这不是重点.
这是理想的行为吗?C++标准委员会是否有任何讨论或计划来解决这种模糊/不愉快?一个例子是要求初始化程序列表构造函数被调用,如下所示:vector<int> u({3})这已经是合法的.
请考虑以下代码段:
#include <iostream>
#include <vector>
void f(std::vector<int>){std::cout << __PRETTY_FUNCTION__ << '\n';}
void f(int x){std::cout << __PRETTY_FUNCTION__ << '\n';}
int main()
{
f({42});
}
Run Code Online (Sandbox Code Playgroud)
如果你运行它,你可以看到f(int)重载是首选,即使std::vector有一个std::initializer_list构造函数(见#8).
问题:为什么转换{42}为int首选(而不是转换为std::vector,{42}是std::initializer_list)?
我用带有标志的 GCC 11.1.0 编译下面的代码-std=c++17。它发生在标准输出上打印initializer_list。
我用带有标志的 MSVC 编译了相同的代码,-std=c++17但它打印了“复制构造函数”。哪个编译器更符合cpp标准?编译器可以自由选择构造函数之一吗?
#include <iostream>
using namespace std;
struct S
{
S(int) { }
S(initializer_list<int>) { cout << "initializer_list"; }
S(const S&) { cout << "copy constructor"; }
operator int() const { return 1; };
};
int main()
{
S s1(20);
S s2{ s1 };
}
Run Code Online (Sandbox Code Playgroud)