这不是一个特定的错误,而是一个一般的 C++ 设计问题。考虑以下场景:
struct MyInterface {};
struct Data : MyInterface{};
struct DataWrapper
{
SomeContainerOf<MyInterface*> getData()
{...}
private:
std::vector<Data> dataStorage;
};
根据我的要求,我需要访问器函数来DataWrapper
返回值的容器MyInterface*
(指针,因为我们真正返回Data
值)。
理想情况下,我们希望在不复制的情况下完成此操作。我想知道做到这一点的最佳方法。据我所知,如下是惯用的 C++ 方法:
auto DataWrapper::getData()
{
return dataStorage
| std::ranges::views::transform(
[](Data& d) -> MyInterface* { return &d; }
);
}
但问题是,很明显这个访问器方法对其返回值是明确的 - 我需要知道这个方法返回这个列表,并且我担心在创建返回类型时auto
,它会有点阻碍。看起来(我在这里可能是错的)你不应该知道 a 的类型view
是什么(与 lambda 类似)
我正在考虑的替代方案是制作一个自定义视图(我可以使类型更加清晰),但重新实现标准中如此明确的东西确实让我很痛苦。对于这种情况,建议采取什么行动?现代 C++ 是否说我应该保留返回值auto
?
如果您担心
auto
表达返回类型过于隐式,那么使用constrainedauto
可能是一个合适的选择。例如我将提出几个半正交建议:
建议 1:通过类型定义阐明返回类型
所以,就这么做吧!不要将函数声明为返回 auto:
detail_
命名空间中的函数等。现在您可以使用它或其类型,而无需重复它。假设您已将其定义为foo()
bar
为您希望此方法的用户了解结果的类型。注意:康桓伟的回答建议使用 constrained
auto
, 以获得类似的效果。这是一个好主意,甚至可能比这个更好(尽管稍微冗长一些)。所以请为他们的答案点赞。建议2:仔细考虑继承的必要性
在你考虑“更大”的
DataWrapper
类之前,持有向量 - 也许你应该考虑是否真的需要使用MyInterface
而不是Data
.您似乎在某种程度上依赖于实际返回Data
的 fromgetData()
- 因此得名。MyInterface
?如果没有,请考虑删除它(或保留它,但除了简化您定义的方式之外不要使用它Data
)。MyInterface
?或者它们在定义时还不知道MyInterface
?如果答案为“否”,请考虑使用 an来表示,std::variant<Data,C1,C2,C3>
的情况,作为 的继承者。C1
C2
C3
MyInterface
如果您可以做到其中任何一个,那么您就可以完全避免对类的需要
DataWrapper
,而只需拥有std::vector<foo>
一些相关的 foo (Data
或变体、类,甚至std::any
)。我并不是说避免继承“更好”;而是说避免继承“更好”。这意味着从一种利弊权衡转向另一种利弊权衡。但许多人只是选择继承,因为这是 C++ 中更传统的处理方式,而不是最适合他们需求的方式。
建议 3:不要退回容器(或范围),成为容器!
DataWrapper
您有一个带有数据向量的值类型类 ( )。现在,你不愿意让人们直接使用该向量,因此DataWrapper
不能将其用作s的C++ 容器Data
。好吧——利用这一点!使其可用作对象继承的容器
MyInheritance
,即MyInheritance
引用!实现 begin、cbegin、size、empty、iterator 以及该指定需求的所有其他部分。也许尽可能在ContigeousContainer中实现所有内容...虽然这会使您的类更加复杂,但您实际上会节省使用的间接复杂性std::ranges
(那里有大量代码),而且 - 您的用户不会必须了解getData()
,因此在使用方面,您可能正在简化事情。由于 lambda 的捕获列表为空,因此默认构造函数可以实例化它。因此,我们可以详细说明返回类型。但由于范围适配器构造函数是显式的,因此会发生一些代码重复:
我
ref_view
在代码片段中使用了它,因为我无法验证第一个类型参数是否应该transform_view
是容器,或者是对容器的引用。但ref_view
满足约束并嵌入对容器的引用。