从一组有限的 class 静态转换为 class

Static cast to a class from a limited set of classes

我想对集合中的 类 之一执行静态转换,作为可变参数模板参数传递:

struct Base {
    int tag_value;
};

struct Derived1 : public Base {
    static constexpr int tag = 1;
    Derived1() : Base{tag} {}
    int foo() { return 100; }
};

struct Derived2 : public Base {
    static constexpr int tag = 2;
    Derived2() : Base{tag} {}
    int foo() { return 200; }
};

struct Derived3 : public Base {
    static constexpr int tag = 3;
    Derived3() : Base{tag} {}
    int foo() { return 300; }
};

template <class ... Candidates, class Fn>
auto apply_casted(Base & base, Fn fn) {
    //compare base::tag_value with each Candidate::tag
    //static_cast<> base to Candidate if match
    //call fn with base casted to matched Derived
    return fn(/*...*/);
}

int main() {
    Derived2 d2;
    Base & b = d2;
    // should throw error (b.tag_value doesn't match neither Derived1::tag nor Derived3::tag
    auto v1 = apply_casted<Derived1, Derived3>(b, [](auto d) {
        return d.foo();
    });
    // should static_cast b to Derived2 and return foo() (200)
    auto v2 = apply_casted<Derived1, Derived2>(b, [](auto d) {
        return d.foo(); //calls Derived2::foo()
    });
}

嗯,我希望代码不言自明。入门代码:https://godbolt.org/z/WfaFt- 我正在寻找 apply_casted 的实现。如何迭代 Candidates... 在编译时可能是最困难的部分。

template <typename Candidate, typename... Candidates, typename Fn>
auto apply_casted(Base& base, Fn&& fn)
{
    if (base.tag_value == Candidate::tag)
    {
        return std::forward<Fn>(fn)(static_cast<Candidate&>(base));
    }

    if constexpr (sizeof...(Candidates) > 0)
    {
        return apply_casted<Candidates...>(base, std::forward<Fn>(fn));
    }
    else
    {
        throw std::runtime_error{"tag_value doesn't match"};
    }
}

DEMO


如果 return 类型可以不同,则应指定一个通用类型作为 apply_casted:

的结果
std::common_type_t<std::invoke_result_t<Fn, Candidate&>
                 , std::invoke_result_t<Fn, Candidates&>...>

可以使用 std::variant 实现类似的功能:

template <typename... Ts> struct overload : Ts... { using Ts::operator()...; };
template <typename... Ts> overload(Ts...) -> overload<Ts...>;

std::variant<Derived1, Derived2, Derived3> v;

v.emplace<Derived2>();

std::visit(overload{ [](Derived2& d) -> int { return d.foo(); },
                     [](auto& d) -> int { throw std::runtime_error{""}; } }, v);

DEMO 2


为了获得更好的性能,您应该使用跳转 table,类似于下面的跳转:

template <typename R, typename F, typename V, typename C>
struct invoker
{
    static R invoke(F&& f, V&& v)
    {
        return f(static_cast<C&&>(v));
    }
};

template <typename Candidate, typename... Candidates, typename Fn>
auto apply_casted(Base& base, Fn&& fn)
{    
    using R = std::common_type_t<std::invoke_result_t<Fn, Candidate&>
                               , std::invoke_result_t<Fn, Candidates&>...>;
    using invoker_t = R(*)(Fn&&, Base&);
    invoker_t arr[]{ &invoker<R, Fn, Base&, Candidate&>::invoke
                   , &invoker<R, Fn, Base&, Candidates&>::invoke... };

    return arr[base.tag_value](std::forward<Fn>(fn), base);
}

DEMO 3

来不及玩了?

您标记了 C++17,因此您可以使用模板折叠(根据 Frank 的建议进行了修改(谢谢!))

template <class ... Candidates, class Fn>
auto apply_casted(Base & base, Fn fn)
 {
   int ret {-1};

   if ( false == ((Candidates::tag == base.tag_value
                     ? ret = fn(static_cast<Candidates&>(base)), true
                     : false) || ...) )
      ; // throw something

   return ret;
 }