我想在可变参数模板类中创建一个函数类型声明。该函数需要接受模板参数列表对应的参数。我试图弄清楚是否有一种方法可以将函数参数列表中除最后一个参数之外的所有参数都约束为 const 引用。最后一个函数参数需要是非常量引用。
下面是一些代码,希望能够展示我正在努力克服的挑战。我选择了
SomeClass<int, int, double>
在这个简单的例子中使用,但是模板类型和模板参数的数量都会改变......
#include <iostream>
template<typename First, typename ...Rest>
struct SomeClass
{
using Fn = void (*)(First&, Rest&...);
using FnConst = void (*)(const First&, const Rest&...);
//using FnLastNonConst = HELP;
};
int main()
{
std::cout << typeid(SomeClass<int, int, double>::Fn).name() << std::endl;
std::cout << typeid(SomeClass<int, int, double>::FnConst).name() << std::endl;
return 0;
}
这里是生成输出,当通过管道
c++filt -t
...
void (*)(int&, int&, double&)
void (*)(int const&, int const&, double const&)
我想弄清楚如何定义
FnLastNonConst
,这样它会产生...
void (*)(int const&, int const&, double&)
我已经尝试过许多涉及 std::conditional 和 helper 类的递归解决方案,但我不知道如何将它们组合到最顶层的函数类型声明中。我已经达到了我的极限,我开始认为这可能是不可能的。任何见解将不胜感激。
您可能需要调整样板文件以使其完全适合您的需求(我知道这是繁琐的部分)但至于基本骨架,它看起来可能像这样:
#include <tuple>
#include <type_traits>
#include <utility>
namespace detail
{
template<typename Tuple, std::size_t ...IDX>
constexpr auto repack_tuple_in_cref(const Tuple t, std::index_sequence<IDX...>)
-> decltype(std::tie(std::get<IDX>(t)...));
template<typename Last, typename ...Args>
constexpr auto add_tuple_tail_ref(Last&&, std::tuple<Args...>)
-> std::tuple<Args..., Last&>;
template<typename ...Args>
constexpr auto void_func_ptr_with_args(std::tuple<Args...>)
-> void (*)(Args...);
}
template<typename ...Args>
constexpr auto func_with_last_non_const()
{
using ValT = std::tuple<Args...>;
using Last = std::tuple_element_t<std::tuple_size_v<ValT>-1, ValT>;
using ConstRefAddedT = decltype(
detail::repack_tuple_in_cref(
std::declval<ValT>(),
std::make_index_sequence<std::tuple_size_v<ValT>-1>{}));
using LastNonConstRefT = decltype(
detail::add_tuple_tail_ref(
std::declval<Last>(), std::declval<ConstRefAddedT>())
);
using FptrT = decltype(detail::void_func_ptr_with_args(
std::declval<LastNonConstRefT>()));
return FptrT{};
}
template <typename First, typename ...Args>
struct S
{
using Fn = decltype(func_with_last_non_const<First, Args...>());
};
int main(int, char const*[])
{
using F = typename S<int, char, double>::Fn;
static_assert(
std::is_same_v<
F,
void (*)(const int&, const char&, double&)>,
"");
return 0;
};
https://godbolt.org/z/zEnGxea37
一旦分解成单独的步骤,其背后的原理就非常简单:将所有类型放入一个元组,将常量/引用/任何内容添加到所需的类型中,再次将它们组合成一个元组并将其内部类型解压到一个元组中最后是函数指针参数。 此外,我假设您使用的是 C++17 或更新版本,我不确定该代码是否可以在该语言的早期版本上编译。
除了@alagner 建议的方法之外,还有另一种方法可以实现此目的,但这可能不是实现该解决方案的最佳方法。
可以如下声明一个函数模板类型
template<typename OneMoreFirst, typename ...OneMoreRest>
using FnCustom = void (*) (OneMoreFirst&, OneMoreRest&...);
用法:
typeid(SomeClass<int, int, double>::FnCustom<const int, const int, double>).name()
并根据需要传递具有常量和非常量的类型。
完整示例请参考:https://godbolt.org/z/v778hG31e
谢谢