此实用程序函数调用在所提供的值上是协变量的函数列表之一。它通过内部对输入值执行dynamic_cast来减少样板,并使用成功转换的输入类型调用第一个函数。必须使用指示返回类型的模板参数来调用它。你认为如何?
#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#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
}发布于 2017-07-07 14:32:09
我相信有更好的方法来解决这个问题。
第一件事是使用动态多态性和使用访问者模式的半双重调度。任何知道多态性是什么的人都很清楚这一点。这项技术很老了。
std::variant<>和std::visit().我认为boost支持较早的C++标准,因为标准标准需要符合C++17的标准库,甚至可能是编译器。
这两种解决方案都比提出的解决方案更能为人们所理解。事实表明,替代解决方案的工程成本要低得多,这使得它们在现实世界中更加可行。
我相信函数内部的垂直间距可以使它们更加可读性。同样,把太长的线分成两部分也很好。并不是所有IDE都可以将模板参数声明拆分为多行,但是手工进行应该不会太困难。
https://codereview.stackexchange.com/questions/167171
复制相似问题