首页 > 解决方案 > 创建一个模板函数,根据模板参数类型条件返回不同的元组类型

问题描述

首先,是否可以根据某些条件返回不同的类型?其次,有没有更简洁的方法来处理这种映射,而不枚举所有可能的 if 和 else?

这里有很多很多(和很多)if's,and's and but's

基本上,我想定义一个不完整的元组,我想自动扩展它以完成一些默认类型。当然,完整性与某些上下文有关。这里,B0 和 B1 将是默认值。并且完整的元组必须有形式<B0 or its derived, B1 or its derived>。实际上,我工作中的元组有两个以上的元素。

struct B0 { };
struct B1 { };

// here, a complete tuple is a tuple that has two elements: 
// of B0 type or its derived as a 1st element and
// of B1 type or its derived as a 2nd element

// templ argumets can be in the order B0, B1 or its derived or absent
template<class Tuple>
CompleteTuple make_complete_tuple()
{
    if (std::tuple_size<Tuple>::value == 0)
        return std::tuple<B0,B1>{};

    else if (std::tuple_size<Tuple>::value == 1)
    {
        using ElemT = std::tuple_element<0, Tuple>::type;

        if (std::is_base_of<B0, ElemT>::value)
            return std::tuple<ElemT, B1>{};
        if (std::is_base_of<B1, ElemT>::value)
            return std::tuple<B0, ElemT>{};
    }

    else if (std::tuple_size<Tuple>::value == 2)
    {
        using Elem0T = std::tuple_element<0, Tuple>::type;
        using Elem1T = std::tuple_element<1, Tuple>::type;

        if (std::is_base_of<B0, Elem0T>::value)
            if(std::is_base_of<B1, Elem1T>::value)
                return std::tuple<Elem0T, Elem1T>{};

        // not handling another conditions, the example is only for conveying idea
    }
}

struct A : public B0 { };
struct C : public B1 { };

int main()
{
    auto complete_tuple0 = make_complete_tuple<std::tuple<A>>();
    // complete_tuple0 shoud be std::tuple<A, B1>;

    auto complete_tuple1 = make_complete_tuple<std::tuple<C>>();
    // complete_tuple1 shoud be std::tuple<B0, C>;

    // etc

    std::cin.get();
    return 0;
}

标签: c++templatestuples

解决方案


C++17 解决方案,C++11 如果你_v usings::value. 不需要不同的基数,但它总是将默认元组中的每个默认基数替换为传递的元组参数中最左边的匹配类型。

#include <tuple>
#include <type_traits>


// Returns the first type from Ts that is derived from Base. Returns Base if there is not one.
template<typename Base,typename...Ts>
struct pick_derived;

// No Ts were derived from Base.
template<typename Base>
struct pick_derived<Base>{
    using type=Base;
};

template<typename Base,typename Derived, typename...Tail>
struct pick_derived<Base,Derived,Tail...>{
    using type = typename std::conditional<std::is_base_of_v<Base,Derived>,
                                           Derived,// Return it. Otherwise continue searching.
                                           typename pick_derived<Base,Tail...>::type>::type;
};

template<typename SourceTuple, typename DefaultTuple>
struct tup_transformer_impl;

template<typename...Ts, typename...Ds>
struct tup_transformer_impl<std::tuple<Ts...>,std::tuple<Ds...>>{
    // Fancy double pack expansion
    // For each default Ds type, try to replace it with a derived type from Ts.
    using type = std::tuple<typename pick_derived<Ds,Ts...>::type...>;
};


#include <iostream>   

struct B0 { };
struct B1 { };

// Tweak this.
using default_tuple = std::tuple<B0,B1>;
template<typename Tuple>
using tup_transform = typename tup_transformer_impl<Tuple,default_tuple>::type;


template<class Tuple>
tup_transform<Tuple> make_complete_tuple()
{
    return {};
}

struct A : public B0 { };
struct C : public B1 { };

 
int main()
{
    auto complete_tuple0 = make_complete_tuple<std::tuple<A>>();
    // complete_tuple0 shoud be std::tuple<A, B1>;
    static_assert(std::is_same_v<decltype(complete_tuple0),std::tuple<A, B1>>);

    auto complete_tuple1 = make_complete_tuple<std::tuple<C>>();
    // complete_tuple1 shoud be std::tuple<B0, C>;
    static_assert(std::is_same_v<decltype(complete_tuple1),std::tuple<B0, C>>);

    std::cin.get();
    return 0;
}

我想你可以这样称呼它:

tup_transform<std::tuple<A>> complete_tuple0;

(将其更改tup_transform<A> complete_tuple0;为非常容易。只需将 tup_transformer_impl 更改为Ds...直接使用,而不是解压缩它们。)

当然,所有这些都要求每种类型都是默认可构造的。如果不是这种情况,我认为如果派生类型至少是可移动的,则该解决方案可以适应工作。在这种情况下,每个派生值都必须传递给make_complete_tuple函数。


推荐阅读