动态转换为函数参数类型 (C++)
Dynamically cast to function argument type (C++)
在 f1()
中,我希望能够将其 foo
参数动态转换为与 f2()
的参数相同的类型(Bar
、Qux
,等等)。这可能吗?
struct Foo {
virtual ~Foo() = default;
};
struct Bar : public Foo {};
struct Qux : public Foo {};
template<class T>
void f1(T f2, Foo &foo) {
// dynamically cast foo to type of f2's argument?
f2(dynamic_cast<Bar &>(foo));
}
int main() {
Bar bar;
Qux qux;
f1([](Bar &bar) {}, bar);
f1([](Qux &qux) {}, qux); // error here!
}
您可以编写一个函数模板来推断函数指针的参数类型:
template<typename Ret, typename Arg>
Arg arg(Ret(*)(Arg));
然后在进行转换时使用它来计算 f2
的参数类型:
f2(dynamic_cast<decltype(arg(+f2))>(foo));
请注意,+
将其作为参数传递给 arg
时会将 lambda 衰减为函数指针。这仅在 lambda f2
只有一个参数且没有任何捕获时有效。
我努力用有状态 lambda 的推导成员函数指针来做到这一点,但最终使用 SFINAE 成功了,它更冗长,但适用于有状态 lambda。
template<class T>
auto f1(T f2, Foo &foo) -> decltype(f2(dynamic_cast<Bar&>(foo))) {
return f2(dynamic_cast<Bar&>(foo));
}
template<class T>
auto f1(T f2, Foo &foo) -> decltype(f2(dynamic_cast<Qux&>(foo))) {
return f2(dynamic_cast<Qux&>(foo));
}
这是一个模板转换作业operator
:
template <typename T>
struct dynamic_caster
{
T *ptr;
dynamic_caster(T &ref) : ptr(&ref) {}
template <typename U>
operator U &() const
{
return *dynamic_cast<std::remove_reference_t<U> *>(ptr);
}
};
template<class T>
void f1(T f2, Foo &foo)
{
f2(dynamic_caster{foo});
}
你不能这样做,至少不能直接这样做。
问题是不受约束的类型 T
(函数对象的类型)可能有多个 operator()
的重载。
它甚至可能是模板化运算符。
这也适用于 lambda(甚至可能是 auto
lambda)。
换句话说,即使有办法,现在 C++ 中也没有足够的内省来做到这一点。
而 IMO,即使您可以,它也可能表明设计存在缺陷。
(@MooingDuck 的答案设计似乎更有前途。)
所以你必须稍微改变你正在做的事情。
例如,您的 lambda 似乎是无状态的,这意味着您可以:
- 使用免费功能。
- 将(无状态)lambda 衰减为函数指针(提示,lambda 衰减 (ab) 使用
+
操作)。
一旦你接受了这一点,接下来就很容易了;
您可以使用 Boost.TypeTraits 来推导函数的(现在唯一)定义的参数。
#include <boost/type_traits.hpp>
struct Foo {
virtual ~Foo() = default;
};
struct Bar : public Foo {};
struct Qux : public Foo {};
template<class T>
void f1(T f2, Foo &foo) {
// dynamically cast foo to type of f2's argument
f2(dynamic_cast<typename boost::function_traits<decltype(*f2)>::arg1_type&>(foo));
}
int main() {
Bar bar;
Qux qux;
f1(+[](Bar &bar) {}, bar);
f1(+[](Qux &qux) {}, qux); // works now!
}
https://godbolt.org/z/r1jf9n8Yf
如果你想要一个更通用的解决方案,你已经定义了一个带有内部或 sfinae 检测到的“第一个参数”的函数对象协议。例如struct F{using arg1_type = ...; auto operator()(arg1_type) const{...}};
.
在 f1()
中,我希望能够将其 foo
参数动态转换为与 f2()
的参数相同的类型(Bar
、Qux
,等等)。这可能吗?
struct Foo {
virtual ~Foo() = default;
};
struct Bar : public Foo {};
struct Qux : public Foo {};
template<class T>
void f1(T f2, Foo &foo) {
// dynamically cast foo to type of f2's argument?
f2(dynamic_cast<Bar &>(foo));
}
int main() {
Bar bar;
Qux qux;
f1([](Bar &bar) {}, bar);
f1([](Qux &qux) {}, qux); // error here!
}
您可以编写一个函数模板来推断函数指针的参数类型:
template<typename Ret, typename Arg>
Arg arg(Ret(*)(Arg));
然后在进行转换时使用它来计算 f2
的参数类型:
f2(dynamic_cast<decltype(arg(+f2))>(foo));
请注意,+
将其作为参数传递给 arg
时会将 lambda 衰减为函数指针。这仅在 lambda f2
只有一个参数且没有任何捕获时有效。
我努力用有状态 lambda 的推导成员函数指针来做到这一点,但最终使用 SFINAE 成功了,它更冗长,但适用于有状态 lambda。
template<class T>
auto f1(T f2, Foo &foo) -> decltype(f2(dynamic_cast<Bar&>(foo))) {
return f2(dynamic_cast<Bar&>(foo));
}
template<class T>
auto f1(T f2, Foo &foo) -> decltype(f2(dynamic_cast<Qux&>(foo))) {
return f2(dynamic_cast<Qux&>(foo));
}
这是一个模板转换作业operator
:
template <typename T>
struct dynamic_caster
{
T *ptr;
dynamic_caster(T &ref) : ptr(&ref) {}
template <typename U>
operator U &() const
{
return *dynamic_cast<std::remove_reference_t<U> *>(ptr);
}
};
template<class T>
void f1(T f2, Foo &foo)
{
f2(dynamic_caster{foo});
}
你不能这样做,至少不能直接这样做。
问题是不受约束的类型 T
(函数对象的类型)可能有多个 operator()
的重载。
它甚至可能是模板化运算符。
这也适用于 lambda(甚至可能是 auto
lambda)。
换句话说,即使有办法,现在 C++ 中也没有足够的内省来做到这一点。 而 IMO,即使您可以,它也可能表明设计存在缺陷。 (@MooingDuck 的答案设计似乎更有前途。)
所以你必须稍微改变你正在做的事情。 例如,您的 lambda 似乎是无状态的,这意味着您可以:
- 使用免费功能。
- 将(无状态)lambda 衰减为函数指针(提示,lambda 衰减 (ab) 使用
+
操作)。
一旦你接受了这一点,接下来就很容易了; 您可以使用 Boost.TypeTraits 来推导函数的(现在唯一)定义的参数。
#include <boost/type_traits.hpp>
struct Foo {
virtual ~Foo() = default;
};
struct Bar : public Foo {};
struct Qux : public Foo {};
template<class T>
void f1(T f2, Foo &foo) {
// dynamically cast foo to type of f2's argument
f2(dynamic_cast<typename boost::function_traits<decltype(*f2)>::arg1_type&>(foo));
}
int main() {
Bar bar;
Qux qux;
f1(+[](Bar &bar) {}, bar);
f1(+[](Qux &qux) {}, qux); // works now!
}
https://godbolt.org/z/r1jf9n8Yf
如果你想要一个更通用的解决方案,你已经定义了一个带有内部或 sfinae 检测到的“第一个参数”的函数对象协议。例如struct F{using arg1_type = ...; auto operator()(arg1_type) const{...}};
.