提问人:Dmitry Kuzminov 提问时间:9/26/2023 最后编辑:Dmitry Kuzminov 更新时间:9/27/2023 访问量:119
如何使用类自己的方法的返回类型定义类的内部别名?
How to define an internal alias of the class using the return type of its own method?
问:
请考虑以下代码:
struct LambdaWrapper {
auto getLambda() {
return []() {
std::cout << "Lambda is called" << std::endl;
};
}
};
void useLambda(auto &&lambda) {
lambda();
}
int main() {
LambdaWrapper w;
useLambda(w.getLambda());
return 0;
}
我同时用于返回类型和函数的参数。但实际上,在此代码中,使用了唯一的类型,这就是返回的类型,因此我可以为该类型定义一个别名:auto
LambdaWrapper::getLambda()
useLambda()
LambdaWrapper::getLambda()
struct LambdaWrapper {
auto getLambda() { ... }
static LambdaWrapper* get();
};
using LambdaType = decltype(LambdaWrapper::get()->getLambda());
void useLambda(const LambdaType &lambda);
现在,我希望使这个别名成为类的一部分:LambdaWrapper
struct LambdaWrapper {
auto getLambda() { ... }
static LambdaWrapper* get();
using LambdaType = decltype(LambdaWrapper::get()->getLambda());
};
void useLambda(const LambdaWrapper::LambdaType &lambda);
但现在我得到了
错误:在扣除“auto”之前使用“auto LambdaWrapper::getLambda()”
如何在类中定义此别名?
更新:我仍然认为我的问题不需要任何额外的细节或澄清,但一些回复者不同意这一点。因此,让我补充更多细节。
我的问题从这里的问题开始:如何定义类型擦除的范围::视图?,我问的是如何类型擦除使用 lambda(可能捕获)的范围视图的复杂组合,以及如果添加或更改更多视图,此组合的类型可能会随着时间的推移而改变。我在以下策略中看到了该问题的解决方案之一:
- 定义一个内联方法(在类的正文中),该方法返回视图的实际复杂组合,而不进行类型擦除。
- 让函数减去实际类型。
- 使用 定义该方法的返回类型的别名。
decltype
- 在使用该复杂视图组合的任何函数中使用该别名;别名会隐藏这种复杂性,即使视图会改变,消费者的代码也不会改变。
我可以通过将此别名定义为类范围之外的类型来做到这一点。我可以这样做,再创建一个间接级别(使用继承或嵌套类)。但是,如果没有像从人工类继承这样的黑客,我的目标就无法实现,这听起来不合逻辑。因此,提出这个问题的目的是找到最优雅的解决方案来实施上述策略。
答:
问题在于类是分两次通过解析的。第一次通过时,编译器会看到声明(而不是定义),但是,由于尚无可用的定义,因此解析失败。这就是为什么编译器在推导返回类型之前告诉您正在使用的原因。auto getLambda()
using LambdaType = decltype(LambdaWrapper::get()->getLambda());
getLamba
getLambda()
如果您使用的是 C++20,则解决方案很简单。您可以将 lambda 的定义移动到 中,假设它没有捕获:decltype
struct LambdaWrapper {
using LambdaType = decltype( []() {
std::cout << "Lambda is called" << std::endl;
});
LambdaType getLambda() {
return {};
}
};
请参阅编译器资源管理器中的实时示例。
适用于旧标准的更通用的解决方案是将 lambda 表达式移动到单独的作用域中,例如基类:
// note: You can also put getLambda() into a namespace, not into a class.
// I suspect your example is overly minimal and you can't actually do that.
struct LambdaProvider {
auto getLambda() {
return []() {
std::cout << "Lambda is called" << std::endl;
};
}
};
// At this point, LambdaProvider has been parsed completely, and we can
// obtain the type of getLambda().
struct LambdaWrapper : LambdaProvider {
using LambdaType = decltype(std::declval<LambdaProvider>().getLambda());
};
请参阅编译器资源管理器中的实时示例。
您甚至可以变成 CRTP(奇怪的重复模板模式),以便您可以访问其中的成员函数。LambdaProvider
LambdaWrapper
评论
this
this
LambdaWrapper*
using LambdaType = ...
LambdaWrapper
评论
getLambda()
{ ... }
auto
class Foo { auto bar() { ... } using baz = decltype(...bar()...); };