我需要帮助创建一个基于可变模板类参数的函数类型声明。

huangapple go评论60阅读模式
英文:

I need help creating a function type declaration based on variadic template class parameters

问题

我想在可变参数模板类中创建一个函数类型声明。该函数需要接受与模板参数列表对应的参数。我试图弄清楚是否有一种方法可以约束除最后一个参数之外的所有函数参数列表中的参数都是const引用。最后一个函数参数需要是非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和辅助类的递归解决方案,但我无法弄清如何将它们结合到最高级的函数类型声明中。我已经达到了我的极限,我开始认为这可能不可能。任何见解将不胜感激。

英文:

I would like to create a function type declaration within a variadic template class. The function needs to take parameters corresponding to the template parameter list. I'm trying to figure out if there is a way to constrain all but the last parameter, in the function parameter list, to be const references. The last function parameter needs to be a non-const reference.

Below is some code which hopefully demonstrates the challenge I'm trying to overcome. I have chosen SomeClass&lt;int, int, double&gt; to use in this simple example, but both the template types and number of template arguments will change...

#include &lt;iostream&gt;

template&lt;typename First, typename ...Rest&gt;
struct SomeClass
{
  using Fn = void (*)(First&amp;, Rest&amp;...);
  using FnConst = void (*)(const First&amp;, const Rest&amp;...);
  //using FnLastNonConst = HELP;
};

int main()
{
  std::cout &lt;&lt; typeid(SomeClass&lt;int, int, double&gt;::Fn).name() &lt;&lt; std::endl;
  std::cout &lt;&lt; typeid(SomeClass&lt;int, int, double&gt;::FnConst).name() &lt;&lt; std::endl;
  return 0;
}

Here is the generate output, when piped through c++filt -t...

void (*)(int&amp;, int&amp;, double&amp;)
void (*)(int const&amp;, int const&amp;, double const&amp;)

I'm trying to figure out how to define FnLastNonConst such that it would generate...

void (*)(int const&amp;, int const&amp;, double&amp;)

I have attempted numerous recursive solutions involving std::conditional and helper classes, but I can't figure out how to bring that together into the top-most function type declaration. I've reached my limits and I'm starting to think this might not be possible. Any insight would be much appreciated.

答案1

得分: 0

以下是代码的中文翻译:

// 你可能需要调整模板代码以使其完全适应您的需求(我知道这部分可能有点繁琐),但基本的骨架可以看起来是这样的:

#include <tuple>
#include <type_traits>
#include <utility>

namespace detail
{
    // 重新打包元组为const引用元组
    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&>;

    // 带有参数的void函数指针
    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{};
}

// S结构体模板
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;
}

该代码的原理相当简单,一旦拆分为单独的步骤:将所有类型放入元组中,将所需的类型添加为const引用,然后再次将其组合成元组,并最终将其内部类型解包为函数指针参数。另外,我假定您正在使用C++17或更新的版本,不确定该代码是否在较早版本的语言中编译。

英文:

You might need to adjust the boilerplate to make it perfectly suit your needs (the cumbersome part, I know) but as for the basic skeleton it can look more-less like this:

#include &lt;tuple&gt;
#include &lt;type_traits&gt;
#include &lt;utility&gt;

namespace detail
{

    template&lt;typename Tuple, std::size_t ...IDX&gt;
    constexpr auto repack_tuple_in_cref(const Tuple t, std::index_sequence&lt;IDX...&gt;)
    -&gt; decltype(std::tie(std::get&lt;IDX&gt;(t)...));

    template&lt;typename Last, typename ...Args&gt;
    constexpr auto add_tuple_tail_ref(Last&amp;&amp;,  std::tuple&lt;Args...&gt;)
    -&gt; std::tuple&lt;Args..., Last&amp;&gt;;

    template&lt;typename ...Args&gt;
    constexpr auto void_func_ptr_with_args(std::tuple&lt;Args...&gt;)
    -&gt; void (*)(Args...);
}

template&lt;typename ...Args&gt;
constexpr auto func_with_last_non_const()
{
    using ValT = std::tuple&lt;Args...&gt;;
    using Last = std::tuple_element_t&lt;std::tuple_size_v&lt;ValT&gt;-1, ValT&gt;;
    using ConstRefAddedT = decltype(
        detail::repack_tuple_in_cref(
            std::declval&lt;ValT&gt;(),
            std::make_index_sequence&lt;std::tuple_size_v&lt;ValT&gt;-1&gt;{}));
    using LastNonConstRefT = decltype(
        detail::add_tuple_tail_ref(
            std::declval&lt;Last&gt;(), std::declval&lt;ConstRefAddedT&gt;())
    );

    using FptrT = decltype(detail::void_func_ptr_with_args(
        std::declval&lt;LastNonConstRefT&gt;()));

    return FptrT{};
}


template &lt;typename First, typename ...Args&gt;
struct S
{
    using Fn = decltype(func_with_last_non_const&lt;First, Args...&gt;());
};




int main(int, char const*[])
{
    using F = typename S&lt;int, char, double&gt;::Fn;

    static_assert(
      std::is_same_v&lt;
        F,
        void (*)(const int&amp;, const char&amp;, double&amp;)&gt;,
    &quot;&quot;);
    return 0;
};

https://godbolt.org/z/zEnGxea37

The principle behind it is quite straightforward once broken down into separate steps: put all the the types is a tuple, add the consts/refs/whatever to the types needed, put it together into a tuple again and unpack its inner types into a function pointer params in the end.
Also I've assumed you are using C++17 or newer, I'm not sure if that code compiles on earlier versions of the language.

答案2

得分: 0

除了@alagner建议的方法之外,还有一种方法可以实现这个目标,但这可能不是实现解决方案的最佳方式。

可以像下面这样声明另一个函数模板类型:

template<typename OneMoreFirst, typename ...OneMoreRest>
using FnCustom = void (*)(OneMoreFirst&, OneMoreRest&...);

用法:

typeid(SomeClass<int, int, double>::FnCustom<const int, const int, double>).name()

然后根据需要传递const和非const类型。

有关完整示例,请参考:https://godbolt.org/z/v778hG31e

谢谢

英文:

There is one more approach to achieve this other than the approach suggested by @alagner, but this might not be the best way to achieve the solution.

One can declare one more function template type as follows

template&lt;typename OneMoreFirst, typename ...OneMoreRest&gt;
using FnCustom = void (*) (OneMoreFirst&amp;, OneMoreRest&amp;...);

Usage:

typeid(SomeClass&lt;int, int, double&gt;::FnCustom&lt;const int, const int, double&gt;).name()

And pass the types with const-ness and non-const-ness as needed.

For complete example please refer to: https://godbolt.org/z/v778hG31e

Thanks

答案3

得分: 0

Using extra layer seems required, for example:

template<typename Tuple, typename Seq>
struct Helper;

template<typename... Ts, std::size_t... Is>
struct Helper<std::tuple<Ts...>, std::index_sequence<Is...>>
{
  using type = void (*) (std::conditional_t<(1 + Is) == sizeof...(Is), Ts&, const Ts&>...);
};

template<typename ...Ts>
struct SomeClass
{
  using Fn = void (*)(Ts&...);
  using FnConst = void (*)(const Ts&...);
  using FnLastNonConst = typename Helper<std::tuple<Ts...>, std::index_sequence_for<Ts...>>::type;
};

Demo

英文:

Using extra layer seems required, for example:

template&lt;typename Tuple, typename Seq&gt;
struct Helper;
template&lt;typename... Ts, std::size_t... Is&gt;
struct Helper&lt;std::tuple&lt;Ts...&gt;, std::index_sequence&lt;Is...&gt;&gt;
{
using type = void (*) (std::conditional_t&lt;(1 + Is) == sizeof...(Is), Ts&amp;, const Ts&amp;&gt;...);
};
template&lt;typename ...Ts&gt;
struct SomeClass
{
using Fn = void (*)(Ts&amp;...);
using FnConst = void (*)(const Ts&amp;...);
using FnLastNonConst = typename Helper&lt;std::tuple&lt;Ts...&gt;, std::index_sequence_for&lt;Ts...&gt;&gt;::type;
};

Demo

huangapple
  • 本文由 发表于 2023年4月4日 11:41:07
  • 转载请务必保留本文链接:https://go.coder-hub.com/75925339.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定