首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >C++ covariant_invoke

C++ covariant_invoke
EN

Code Review用户
提问于 2017-07-02 16:12:58
回答 1查看 121关注 0票数 4

此实用程序函数调用在所提供的值上是协变量的函数列表之一。它通过内部对输入值执行dynamic_cast来减少样板,并使用成功转换的输入类型调用第一个函数。必须使用指示返回类型的模板参数来调用它。你认为如何?

covariant_invoke.hpp

代码语言:javascript
复制
#ifndef UTILITIES_COVARIANT_INVOKE_HPP
#define UTILITIES_COVARIANT_INVOKE_HPP

namespace details {
    template <typename T>
    struct invoke_type_impl {
        static_assert(!std::is_same_v<T, T>, "template instantiation failed");
    };

    template <typename T, typename U, typename V>
    struct invoke_type_impl<T (U::*)(V const &) const> {
        typedef V in_t;
        typedef T out_t;
    };

    template <typename T>
    struct invoke_type {
        typedef typename invoke_type_impl<decltype(&T::operator ())>::in_t in_t;
        typedef typename invoke_type_impl<decltype(&T::operator ())>::out_t out_t;
    };
}

template<typename U, typename T, typename V1, typename std::enable_if<!std::is_same<U, void>::value, int>::type = 0>
U covariant_invoke(T const & value, V1 const & func1) {
    typedef typename details::invoke_type<V1>::in_t in_t;
    auto const * asU1 = dynamic_cast<in_t const *>(&value);
    if (asU1 != nullptr) {
        return func1(*asU1);
    }
    throw std::runtime_error("no suitable function");
}

template<typename U, typename T, typename V1, typename V2, typename... Vs, typename std::enable_if<!std::is_same<U, void>::value, int>::type = 0>
U covariant_invoke(T const & value, V1 const & func1, V2 const & func2, Vs const &... funcs) {
    typedef typename details::invoke_type<V1>::in_t in_t;
    auto const * asU1 = dynamic_cast<in_t const *>(&value);
    if (asU1 != nullptr) {
        return func1(*asU1);
    }
    return covariant_invoke<U>(value, func2, funcs...);
}

template<typename U, typename T, typename V1, typename std::enable_if<std::is_same<U, void>::value, int>::type = 0>
void covariant_invoke(T const & value, V1 const & func1) {
    typedef typename details::invoke_type<V1>::in_t in_t;
    static_assert(std::is_same<typename details::invoke_type<V1>::out_t, void>::value, "function does not return void");
    auto const * asU1 = dynamic_cast<in_t const *>(&value);
    if (asU1 != nullptr) {
        func1(*asU1);
        return;
    }
    throw std::runtime_error("no suitable function");
}

template<typename U, typename T, typename V1, typename V2, typename... Vs, typename std::enable_if<std::is_same<U, void>::value, int>::type = 0>
void covariant_invoke(T const & value, V1 const & func1, V2 const & func2, Vs const &... funcs) {
    typedef typename details::invoke_type<V1>::in_t in_t;
    static_assert(std::is_same<typename details::invoke_type<V1>::out_t, void>::value, "function does not return void");
    auto const * asU1 = dynamic_cast<in_t const *>(&value);
    if (asU1 != nullptr) {
        func1(*asU1);
        return;
    }
    covariant_invoke<U>(value, func2, funcs...);
}

#endif //UTILITIES_COVARIANT_INVOKE_HPP

示例用法

代码语言:javascript
复制
#include <iostream>

class A {
public:
    virtual ~A() = default;
};

class B : public A {
public:
    explicit B(int value) : i(value) {}
    virtual ~B() = default;
    int i;
};

class C : public A {
public:
    explicit C(char* value) : strPtr(value) {}
    virtual ~C() = default;
    char* strPtr;
};

class D : public A {
public:
    virtual ~D() = default;
};

B b = B(1337);
C c = C("Hello, world!");
D d;

// test the non-void return overload
void test_non_void_return(A const & a) {
    std::cout << covariant_invoke<std::string>(a,
        [&](B const & value) { return std::to_string(value.i); },
        [&](C const & value) { return value.strPtr; });
}

// test the void return overload
void test_void_return(A const & a) {
    covariant_invoke<void>(a,
        [&](B const & value) { std::to_string(value.i); },
        [&](C const & value) { value.strPtr; });
}

void test() {
    test_non_void_return(b);
    test_non_void_return(c);
    test_non_void_return(d); //will throw, because there is no lambda to handle D values

    test_void_return(b);
    test_void_return(c);
    test_void_return(d); //will throw, because there is no lambda to handle D values
}
EN

回答 1

Code Review用户

发布于 2017-07-07 14:32:09

我相信有更好的方法来解决这个问题。

Alt.解决方案#1

第一件事是使用动态多态性和使用访问者模式的半双重调度。任何知道多态性是什么的人都很清楚这一点。这项技术很老了。

Alt.解决方案2

std::variant<>std::visit().我认为boost支持较早的C++标准,因为标准标准需要符合C++17的标准库,甚至可能是编译器。

这两种解决方案都比提出的解决方案更能为人们所理解。事实表明,替代解决方案的工程成本要低得多,这使得它们在现实世界中更加可行。

代码评审

代码格式

我相信函数内部的垂直间距可以使它们更加可读性。同样,把太长的线分成两部分也很好。并不是所有IDE都可以将模板参数声明拆分为多行,但是手工进行应该不会太困难。

票数 1
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/167171

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档