使用带索引的包扩展-它是UB吗?



下面的代码似乎"工作-然而,我有点担心我在标记点的未指定行为的领域。如果我是,有人能给我一个骨头,这样我就可以确保我不会有它突然打破当我改变编译器?

这样做的目的(在不清楚的情况下)是,我想生成一个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);
};
}

可能更容易。

相关内容

  • 没有找到相关文章

最新更新