下面的代码似乎"工作-然而,我有点担心我在标记点的未指定行为的领域。如果我是,有人能给我一个骨头,这样我就可以确保我不会有它突然打破当我改变编译器?
这样做的目的(在不清楚的情况下)是,我想生成一个std::函数,它能够包装另一个——但是以一种稍微不同的方式处理参数。
/// Some collection of arguments generated at runtime.
class ArgCollection
{
int argCount;
std::variant *arguments;
}
/// generate the wrapping fn
template<class ...Args>
std::function<void(ArgCollection)> GetConvert(std::function<void(Args...)> thing)
{
constexpr std::size_t argCount = sizeof...(Args);
return [argCount, method](const ArgCollection& args) -> void {
if (args.numArguments != argCount)
throw std::invalid_argument("Invalid number of arguments");
int count = 0; <------------ I fear about the usage of this variable.
auto convertedArgs = std::make_tuple(ConvertArg<Args>(args, count++)...);
std::apply(method, convertedArgs);
};
}
/// helper const & reference stripping
template<typename T>
using base_type = typename std::remove_cv<typename std::remove_reference<T>::type>::type;
/// Get the idx'th argument, and convert it to what we can hand to the function
template<class T>
static base_type<T> ConvertArg(const ArgCollection &args, int idx)
{
return base_type<T>(args[idx]);
}
auto convertedArgs = std::make_tuple(ConvertArg<Args>(args, count++)...);
的增量之间的相对顺序是不确定的。编译器可以自由地以任何顺序执行它们,并且可以像蝴蝶扇动翅膀一样改变顺序。(在c++17之前,保证比这更糟糕)
在c++20中有一个简单的解决方法:
constexpr std::size_t argCount = sizeof...(Args);
return [&]<std::size_t...Is>(std::index_sequence<Is...>){
return [argCount, method](const ArgCollection& args) -> void {
if (args.numArguments != argCount)
throw std::invalid_argument("Invalid number of arguments");
auto convertedArgs = std::make_tuple(ConvertArg<Args>(args, Is)...);
std::apply(method, convertedArgs);
};
}( std::make_index_sequence<sizeof...(Args)>{} );
创建一个索引序列对象,并将其解包到函数的lambda中。
在c++17中,你基本上需要帮助函数来构建和解包索引。
template<auto x>
using constant_t = std::integral_constant<std::decay_t<decltype(x)>, x>;
template<auto x>
constexpr constant_t<x> constant_v={};
template<std::size_t...Is, class F>
decltype(auto) index_over( std::index_sequence<Is...>, F&& f ) {
return f( constant_v<Is>... );
}
template<std::size_t N, class F>
decltype(auto) index_upto(F&& f) {
return index_over( std::make_index_sequence<N>{}, std::forward<F>(f) );
}
那么你的代码变成:
constexpr std::size_t argCount = sizeof...(Args);
return index_upto<argCount>([&](auto...Is){
return [argCount, method, Is...](const ArgCollection& args) -> void {
if (args.numArguments != argCount)
throw std::invalid_argument("Invalid number of arguments");
auto convertedArgs = std::make_tuple(ConvertArg<Args>(args, Is)...);
std::apply(method, convertedArgs);
};
});
或somesuch。
您还可以编写一个更常规的辅助函数,将索引序列传递给它。
最后,您可以依赖基于{}
的初始化是有序的。
template<class ...Args>
std::function<void(ArgCollection)> GetConvert(std::function<void(Args...)> thing)
{
constexpr std::size_t argCount = sizeof...(Args);
return [argCount, method](const ArgCollection& args) -> void {
if (args.numArguments != argCount)
throw std::invalid_argument("Invalid number of arguments");
int count = 0; <------------ I fear about the usage of this variable.
auto convertedArgs = std::tuple{ConvertArg<Args>(args, count++)...};
std::apply(method, convertedArgs);
};
}
可能更容易。