C ++模板在模板函数实例化时未知类型

时间:2017-03-15 12:36:46

标签: c++ c++11 templates c++14

class BaseClass{
    public:
    std::string name;

    BaseClass(std::string typeName) : name(typeName) {};
    std::string GetType(){ return name; }
    };

template<typename T>
class DerivedClass : public BaseClass{
    public:
    T val;
    DerivedClass(std::string typeName, T arg) : BaseClass(typeName), val(arg) {};
    };

template<typename U, typename L>
void foo1(U & arg1, L & arg2)
{
    std::cout << arg1.val + arg2.val << std::endl;
}

void foo(BaseClass *arg1, BaseClass *arg2)
{
    if(arg1->GetType() == "Int")
    {
        auto p1 = (DerivedClass<int>*)arg1;
        if(arg2->GetType() == "Int")
        {
            auto p2 = (DerivedClass<int>*)arg2;
            foo1(*p1, *p2);
        }
        else if(arg2->GetType() == "Float")
        {
            auto p2 = (DerivedClass<float>*)arg2;
            foo1(*p1, *p2);
        }
        //else if () AND SO ON ........
    }
    else if(arg1->GetType() == "Float")
    {
        auto p1 = (DerivedClass<float>*)arg1;
        if(arg2->GetType() == "Int")
        {
            auto p2 = (DerivedClass<int>*)arg2;
            foo1(*p1, *p2);
        }
        else if(arg2->GetType() == "Float")
        {
            auto p2 = (DerivedClass<float>*)arg2;
            foo1(*p1, *p2);
        }
    }
    //else if () AND SO ON .....
}

int main()
{
   BaseClass *k1 = new DerivedClass<int>("Int", 2);
   BaseClass *k2 = new DerivedClass<float>("Float", 4.32);

   foo(k1, k2);

   return 0;
}

我遇到了与上述测试案例类似的问题。 在函数foo中,有没有更优雅的方法解析多个类型到ifs的梯形图来运行模板函数? 如果对于1或2个参数没有那么糟糕的阶梯,那么(参数计数)^(类型计数);

2 个答案:

答案 0 :(得分:1)

您可以使用一些模板魔法为您生成if ... else链。首先,编写一个通用的编译时迭代函数:

template <typename TF, typename... Ts>
void for_each_arg(TF&& f, Ts&&... xs)
{
    return (void)(int[]){(f(std::forward<Ts>(xs)), 0)...};
}

您还需要将类型绑定到字符串的内容:

template <typename T>
struct bound_type 
{ 
    using type = T; 
    std::string _name;

    bound_type(std::string name) : _name{std::move(name)} { }
};

然后你可以用它来检查你感兴趣的类型:

void foo(BaseClass *arg1, BaseClass *arg2)
{
    const auto for_bound_types = [](auto&& f)
    { 
        return for_each_arg(std::forward<decltype(f)>(f), 
                            bound_type<int>{"Int"},
                            bound_type<float>{"Float"},
                            bound_type<double>{"Double"});
    };

    for_bound_types([&](const auto& t1)
    {
        if(arg1->GetType() != t1._name) return;

        for_bound_types([&](const auto& t2)
        {
            if(arg2->GetType() != t2._name) return;

            using t1_type = typename std::decay_t<decltype(t1)>::type;
            using t2_type = typename std::decay_t<decltype(t2)>::type;

            auto& p1 = static_cast<DerivedClass<t1_type>&>(*arg1);
            auto& p2 = static_cast<DerivedClass<t2_type>&>(*arg2);
            foo1(p1, p2);
        }); 
    }); 
}

live wandbox example

我想避免在这里使用std::string,但编译时字符串在C ++中是不合理的。可以使用像typestring这样的东西。

在C ++ 17中,由于 fold表达式for_each_arg将是多余的。 std::apply也可用于实施for_bound_types

const auto for_bound_types = [](auto&& f)
{ 
    return std::apply([&](auto... xs){ (f(xs), ...); }, 
        std::make_tuple(bound_type<int>{"Int"},
                        bound_type<float>{"Float"},
                        bound_type<double>{"Double"}));
};

答案 1 :(得分:1)

生成的代码为stripWhitespaceY,但您可以让编译器为您完成。

获取args^typesstd::variant,或自行撰写。

让每个参数为您提供带有副本的boost::variant,或带有指向元素或其自身的指针的variant

对这些变体使用variantstd::visit

boost::apply_visitor

现在我们得到:

template<typename T>
class DerivedClass;

class BaseClass{
public:
  std::string name;
  virtual std::variant< DerivedClass<int>*, DerivedClass<double>* >
  self() = 0;

  BaseClass(std::string typeName) : name(typeName) {};
  std::string GetType(){ return name; }
};

template<typename T>
class DerivedClass : public BaseClass{
public:
  T val;
  DerivedClass(std::string typeName, T arg) : BaseClass(typeName), val(arg) {};
  std::variant< DerivedClass<int>*, DerivedClass<double>* >
  self() overload { return this; }
  std::variant< DerivedClass<int> const*, DerivedClass<double> const* >
  self() const overload { return this; }
};

现在这会将问题归结为&#34;如何撰写void foo(BaseClass *arg1, BaseClass *arg2) { auto a1 = arg1->self(); auto a2 = arg2->self(); auto foo_overloads = [](auto&&...args)->decltype(auto){ return foo(decltype(args)(args)...); }; std::visit( foo_overloads, a1, a2 ); } std::visit&#34;。但两者的代码都可以在互联网上找到。两者都以std::variantstd::experimental::variant提供。

维护类型列表std::experimental::visit int可以在类型列表中完成,并从中生成列表double

如果您希望列表以运行时成本定制和动态,您可以改为使用类型列表,在使用点构建从variant<DerivedClass<int>*, DerivedClass<double>*>std::string的地图(列出您的类型)支持那里,并在那里做同样的事情。