[英]how to forward the types of tuple to specialize other template?
currently I'm working on a dynamic container structure, which represents one pod value or has vector of pointers with same container type. 目前我正在研究一个动态容器结构,它代表一个pod值或者具有相同容器类型的指针向量。 The container has an interface
optional<T> expect_value<T>()
。 For pod types the implemention is simple. 容器有一个接口
optional<T> expect_value<T>()
。对于pod类型,实现很简单。 For the non pod value, I would call expect_value<tuple<args...>>()
, the args
would be tuple as well. 对于非pod值,我将调用
expect_value<tuple<args...>>()
, args
也将是元组。 But when implement this function, I come across a trouble: how to redirect a.expect_value<tuple<args...>>()
to a.expect_value_tuple<args...>>()
. 但是当实现这个函数时,我遇到了一个麻烦:如何将
a.expect_value<tuple<args...>>()
重定向到a.expect_value_tuple<args...>>()
。 For example, the call to a.expect_value<tuple<int,int>()
would return the result of a.expect_value_tuple<int, int>()
. 例如,对
a.expect_value<tuple<int,int>()
的调用将返回a.expect_value_tuple<int, int>()
。 Because the argument is empty, I cant use the type deduce of unpacked arguments. 因为参数是空的,所以我不能使用解压缩参数的类型推导。 Then the whole project just cant progress any more.
然后整个项目再也无法进展了。 Any ideas?
有任何想法吗? Below is the minimal example for my intention.
以下是我的意图的最小例子。
#include <tuple>
#include <vector>
#include <optional>
#include <functional>
using namespace std;
template<typename T>
struct is_tuple_impl : std::false_type {};
template<typename... Ts>
struct is_tuple_impl<std::tuple<Ts...>> : std::true_type {};
template<typename T>
struct is_tuple : is_tuple_impl<std::decay_t<T>> {};
class my_container;
template<typename... args, size_t... arg_idx>
optional<tuple<args>...> get_tuple_value_from_vector(const vector<my_container*>& v_list, std::index_sequence<arg_idx...>)
{
auto temp_result = make_tuple((*v_list[arg_idx]).expect_value<arg>()...);
if(!(get<arg_idx>(temp_result) &&...))
{
return nullopt;
}
return make_tuple(get<arg_idx>(temp_result).value()...);
}
class my_container
{
public:
int value_type; // 1 for v_int 2 for v_list 0 empty
union
{
int v_int;
};
vector<my_container*> v_list;
template<typename T>
optional<T> expect_simple_value();
template<typename... args>
optional<tuple<args...>> expect_tuple_value();
template<typename T>
optional<T> expect_value();
};
template <typename T>
optional<T> my_container::expect_simple_value()
{
return nullopt;
}
template <>
optional<int> my_container::expect_simple_value()
{
if(value_type == 1)
{
return v_int;
}
return nullopt;
}
template<typename... args>
optional<tuple<args...>> my_container::expect_tuple_value()
{
if(v_list.size() == 0)
{
return nullopt;
}
for(const auto i: v_list)
{
if(!i)
{
return nullopt;
}
}
auto the_tuple_size = sizeof...(args);
if(v_list.size() != the_tuple_size)
{
return nullopt;
}
return get_tuple_value_from_vector<args...>(v_list, index_sequence_for<args...>{});
}
template <typename T>
optional<T> my_container::expect_value()
{
if(is_tuple<T>::value)
{
return expect_tuple_value<T>();
}
else
{
return expect_simple_value<T>();
}
}
int main()
{
my_container test_value;
test_value.value_type = 1;
test_value.v_int = 1;
auto result = test_value.expect_value<tuple<int, int>>();
if(result)
{
return 0;
}
else
{
return 1;
}
}
the heart of the problem is the line return expect_tuple_value<T>();
问题的核心是行
return expect_tuple_value<T>();
When logic goes there, the T should be tuple<args...>
, but what I want is return return expect_tuple_value<args...>()
. 当逻辑进入那里时,T应该是
tuple<args...>
,但我想要的是return expect_tuple_value<args...>()
。
What about using template argument deduction and overload resolution through partial ordering of function template: 如何通过函数模板的部分排序来使用模板参数推导和重载解析:
class my_container
{
public:
template<class T> optional<T> expect_value_simple();
template<class...Args> optional<tuple<Args...>> expect_value_tuple();
private:
template<class T> struct deduce_type{};
template<typename T>
auto expect_value_dispatching(deduce_type<T>){
return expect_value_simple<T>();
}
template<typename...Args>
auto expect_value_dispatching(deduce_type<tuple<Args...>>){
return expect_value_tuple<Args...>();
}
public:
template<typename T>
auto expect_value(){
return expect_value_dispatching(deduce_type<T>{});
}
};
The if
before the line in question should be a constexpr if
. 有问题的行之前的
if
应该是constexpr if
。
Unpacking of types is annoying to do without using a class helper. 在不使用类助手的情况下解压缩类型很烦人。 I can do it with some fancy c++14 lambda action tho.
我可以用一些奇特的c ++ 14 lambda动作来做到这一点。
template<class T>
struct tag_t{using type=T;};
template<class Tag>
using type=typename Tag::type;
template<class Tuple>
struct unpack_tuple;
template<class...Ts>
struct unpack_tuple<std::tuple<Ts...>> {
template<class F>
decltype(auto) operator()(F&& f)const {
return std::forward<F>(f)( tag_t<Ts>{}... );
}
};
#define TYPE_FROM(...) \
type< std::decay_t<decltype(__VA_ARGS__)> >
then we get 然后我们得到
if constexpr(is_tuple<T>::value)
{
return unpack_tuple<T>{}([&](auto...tags){
return expect_tuple_value<TYPE_FROM(tags)...>();
});
}
else
{
return expect_simple_value<T>();
}
and done. 并做了。
The core issue here is that you need to do argument deduction at least once to go back from a type to its variadic parameters. 这里的核心问题是你需要至少进行一次参数推导才能从一个类型返回到它的可变参数。 To do that, you must pass some instance of such a variadically-templated type to a function - but it does not have to be the original one.
要做到这一点,你必须将这种变量模板类型的一些实例传递给一个函数 - 但它不必是原始函数。
Yakk 's solution does this via a variadic lambda that is passed instances of tag-types (one per tuple type). Yakk的解决方案是通过一个可变参数lambda来实现的,该lambda传递了标签类型的实例(每个元组类型一个)。 The advantage here is that you can use a lambda instead of an explicit intermediary function every time.
这里的优点是每次都可以使用lambda而不是显式中间函数。
Oliv 's solution uses a monostate type which we can instantiate and pass to a function for type deduction. Oliv的解决方案使用monostate类型,我们可以实例化它并传递给函数进行类型推导。 It's much cleaner but requires such an intermediary function for every use case.
它更干净,但每个用例都需要这样的中介功能。
Here is a (more or less theoretical) version combining both, using templated variadic lambdas ( C++20 , and they apparently don't even have clang support as of now): 这是一个(或多或少的理论)版本结合两者,使用模板化的可变参数lambda( C ++ 20 ,他们显然现在甚至没有clang支持 ):
template<class... Args>
struct MonostateTuple
{};
template<class... Args>
auto tupleToMonostate(std::tuple<Args...>)
{
return MonostateTuple<Args...>{};
}
template<class T, class F>
auto unpack_tuple(F&& f)
{
using MT = decltype(tupleToMonostate(std::declval<T>()));
return std::forward<F>(f)(MT{});
}
/// User code
template<class Tuple>
auto foo()
{
return unpack_tuple<Tuple>([&] <typename... Args> (MonostateTuple<Args...>) {
return expect_tuple_value<Args...>();
});
}
It's a bit more ugly in the lambda signature (not to mention the lack of compiler support again) but theoretically combines both advantages. 它在lambda签名中更难看(更不用说再次缺乏编译器支持),但理论上结合了两个优点。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.