此问题建立在此@FredOverflow's question。
之上澄清:需要
initializer_list
方法,因为VC++2012 has a bug阻止了命名空间参数的转发扩展。_MSC_VER <= 1700
有错误。
我编写了一个可变参数模板函数,它可以折叠类型化容器中的任意数量的参数。我使用类型的构造函数将可变参数转换为可使用的值。例如。 _variant_t
:)
我在MySql
C ++库中为一次打击中的预准备语句推送参数时需要这个,而我的MySqlVariant
将输入数据转换为MYSQL_BIND
s。因为我可以使用BLOB
,所以当我可以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;
}
使用非常简单:
// 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 :)
我还上传了一个完整的测试 on IDEone ^,显示std::string
的内存使用此功能正常移动。 (我会把它全部粘贴在这里,但它有点长......)
只要_variant_t
(或任何最终包装对象)具有正确的构造函数,它就很棒。如果数据可以移出,那就更好了。当我在正确的方向上测试它和std::move
时,它几乎可以工作:)
我的问题很简单:
std::move
默认情况下initializer_list
不起作用,那么我在这里做的是:非法,不道德,hacky ......或者只是错误的吗 PS :我是一名自学成才的Windows Native C++
开发人员,对标准一无所知。
^我的借口,如果我在这里做非标准的事情。
更新
谢谢大家,我现在有答案和解决方案(短期和长期)。
我喜欢SO的C ++ 11方面。 这里知识渊博的人......
答案 0 :(得分:8)
在一般情况下,遗憾的是,这是未定义的行为。在§8.5.4/ 5,强调我的:
类型为
std::initializer_list<E>
的对象是从初始化列表构造的,就好像实现分配了N
类型为const E
的临时数组,其中{ {1}}是初始化列表中的元素数。使用初始化列表的相应元素对该数组的每个元素进行复制初始化, 构造N
对象以引用该数组。
如果您看到std::initializer_list<E>
,则可以表现为std::initializer_list<E>
。
因此,当const E[N]
离开const_cast
时,您正在查看对const
对象的可变引用。对const
对象的任何修改都是未定义的行为。
当您移动const
时,您正在修改std::string
对象。不幸的是,未定义行为的行为之一似乎是正确的行为。但这在技术上是不确定的。
请注意,当您const
进入另一个时, 是明确定义的,因为std::move(int)
只能复制,因此移动不执行任何操作,并且不会修改int
个对象。但总的来说,它是未定义的。
答案 1 :(得分:1)
为分享我痛苦的人找到了另一种解决方案:
#if _MCS_VER <= 1700
// Use the code in the OP!
// VS 2012- stuff comes here.
#else
// VS 2013+ stuff comes here.
template <typename Output_t>
inline Output_t& Compact(Output_t& aOutput){
return aOutput;
}
template <typename Output_t, typename First_t>
inline Output_t& Compact(Output_t& aOutput, const First_t& aFirst){
aOutput.emplace_back(aFirst);
return aOutput;
}
template <typename Output_t, typename First_t>
inline Output_t& Compact(Output_t& aOutput, First_t&& aFirst){
aOutput.emplace_back(std::move(aFirst));
return aOutput;
}
template <typename Output_t, typename First_t, typename ...Next_t>
inline Output_t& Compact(Output_t& aOutput, const First_t& aFirst, Next_t&& ...aNext){
aOutput.emplace_back(aFirst);
return Compact(aOutput, std::forward<Next_t>(aNext)...);
}
template <typename Output_t, typename First_t, typename ...Next_t>
inline Output_t& Compact(Output_t& aOutput, First_t&& aFirst, Next_t&& ...aNext){
aOutput.emplace_back(std::move(aFirst));
return Compact(aOutput, std::forward<Next_t>(aNext)...);
}
#endif // _MCS_VER <= 1700
PS : VC ++ 2012 CTPnov2012有一个BUG阻止它在namespaced classes上工作。因此,没有{{1}的初始解决方案}必须做。 我的所有代码都是命名空间。 VC2013在理论上已经修复了......因此我会在升级时切换代码。
答案 2 :(得分:1)
您可以将专业化减少一个。这个“通用引用”专门化也应该包含左值引用,在这种情况下std::move
将不执行任何操作。
template <typename Output_t, typename First_t>
inline Output_t& Compact(Output_t& aOutput, First_t&& aFirst){
aOutput.emplace_back(std::forward<First_t>(aFirst));
return aOutput;
}
资料来源:Scott Meyers在GoingNative2013上发表演讲;在this accu article
中详细说明