这个问题建立在这个@ FredOverflow的问题上.
澄清:
initializer_list
需要方法,因为VC++ 2012有一个错误,阻止了命名空间参数的转发扩展._MSC_VER <= 1700
有错误.
我编写了一个可变参数模板函数,它可以折叠类型化容器中的任意数量的参数.我使用类型的构造函数将可变参数转换为可使用的值.例如_variant_t
:)
MySql
在一次打击MySqlVariant
中将参数推送到预准备语句时,我需要这个用于我的C++库,而我将输入数据转换为MYSQL_BIND
s.因为我可以使用BLOB
s,所以当我可以move&&
使用大容器时,我希望尽可能避免复制构造.
我做了一个简单的测试,发现了initialize_list
确实copy-construct
为存储的元素,当它超出范围破坏它们.完美...然后我试图将数据移出,initializer_list
并且令我惊讶的是,它并lvalues
没有rvalues
像我预期的那样使用std::move
.
有趣的是,就在Going Native 2013之后发生了明显的警告我,移动不动,前进不会前进 ...... 就像水,我的朋友 - 留在思考的深层.
但这并没有阻止我:)我决定const_cast
了initializer_list
价值观,仍然将它们移出去.需要强制执行驱逐令.这是我的实施:
template <typename Output_t, typename ...Input_t>
inline Output_t& Compact(Output_t& aOutput, Input_t&& ...aInput){
// should I do this? makes sense...
if(!sizeof...(aInput)){
return aOutput;
}
// I like typedefs as they shorten the code :)
typedef Output_t::value_type Type_t;
// can be either lvalues or rvalues in the initializer_list when it's populated.
std::initializer_list<Type_t> vInput = { std::forward<Input_t>(aInput)... };
// now move the initializer_list into the vector.
aOutput.reserve(aOutput.size() + vInput.size());
for(auto vIter(vInput.begin()), vEnd(vInput.end()); vIter != vEnd; ++vIter){
// move (don't copy) out the lvalue or rvalue out of the initializer_list.
// aOutput.emplace_back(std::move(const_cast<Type_t&>(*vIter))); // <- BAD!
// the answer points out that the above is undefined so, use the below
aOutput.emplace_back(*vIter); // <- THIS is STANDARD LEGAL (copy ctor)!
}
// done! :)
return aOutput;
}
Run Code Online (Sandbox Code Playgroud)
使用它很容易:
// You need to pre-declare the container as you could use a vector or a list...
// as long as .emplace_back is on duty!
std::vector<MySqlVariant> vParams;
Compact(vParams, 1, 1.5, 1.6F, "string", L"wstring",
std::move(aBlob), aSystemTime); // MySql params :)
Run Code Online (Sandbox Code Playgroud)
我还上传了一个 关于IDEone ^的完整测试,它显示了std::string
使用此功能正常移动的内存.(我会把它粘贴在这里,但它有点长...)
只要_variant_t
(或任何最终的包装对象)具有正确的构造函数,它就很棒.如果数据可以移出,那就更好了.它几乎可以工作,因为我测试它和std::move
正确方向的东西:)
我的问题很简单:
std::move
默认不起作用initializer_list
,我在这里做的是:非法的,不道德的,hacky ......或者只是简单的错误?PS:我是一名自学成才的Windows Native C++
开发人员,对标准一无所知.
^我的借口,如果我在这里做非标准的事情.
UPDATE
谢谢大家,我现在有答案和解决方案(短期和长期).
我喜欢SO的C++ 11方面. 这里知识渊博的人......
在一般情况下,遗憾的是,这是未定义的行为.在§8.5.4/ 5,强调我的:
类型的对象
std::initializer_list<E>
是从初始化列表构造的,就好像实现分配了一个临时N
的类型元素数组const E
,其中N
是初始化列表中元素的数量.使用初始化列表的相应元素对该数组的每个元素进行复制初始化,并std::initializer_list<E>
构造该对象以引用该数组.
在你看到的地方std::initializer_list<E>
,你可以表现得好像是一个const E[N]
.
所以,当你const_cast
离开时const
,你正在看一个const
对象的可变引用.对const
对象的任何修改都是未定义的行为.
当你移动它时std::string
,你正在修改一个const
对象.不幸的是,未定义行为的行为之一似乎是正确的行为.但这在技术上是不确定的.
请注意,当您std::move(int)
进入另一个时,这是明确定义的,因为int
只能复制,因此移动不执行任何操作,也不会const
修改任何对象.但总的来说,它是未定义的.