【问题标题】:How to use decltype on private class-members?如何在私有类成员上使用 decltype?
【发布时间】:2018-09-06 19:25:37
【问题描述】:

我有一个带有一些“毛茸茸”私有字段的课程。每个都有访问器函数(getter 和 setter)。

private:
    array<double, 9>         foo;
public:
    const array<double, 9> & getFoo() const { return foo; }
    void                     setFoo(const array<double, 9> & _foo) { foo = _foo; }

我真的很想不要在别处重复array&lt;double, 9&gt; -- 使用decltype 来指代字段的类型,无论它可能是什么。

不幸的是,简单地调用decltype(instance.foo) 在课堂外不起作用,因为foo 是私有的。

幸运的是,decltype(getFoo())几乎可以工作——getFoo 是公开的并且必须具有相同的类型。

不幸的是,上面的“差不多”还不够好——getFoo 的类型实际上是一个引用array&lt;double, 9&gt; &amp;)。

如何在类外部的代码中获取实际类型,以便可以调用 setter 函数:

  SOMETHING values;
  for (auto &i : values)
      i = something();
  instance.setFoo(values);

【问题讨论】:

  • 为什么没有公共类型别名,比如using my_array std::array&lt;double, 9&gt;;?然后你可以在任何地方都使用my_array,而不必每次都使用decltype
  • @user463035818,在调用setFoo()之前不要调用getFoo()——foo可能还没有初始化...@FrançoisAndrieux,我有几个不同的这样的成员类——为每个类设置一个单独的类型会有点混乱:(当然,除非没有其他办法。
  • 成员是内部表示,方法是定义接口——你不应该盲目地让你的方法代表成员。您也不应该为每个成员设置 getter 和 setter - 这与将它们公开的复杂方式基本相同。
  • 如果你真的坚持避免使用类型别名,你可以删除参考:std::remove_reference_t&lt;decltype(getFoo())&gt; values;
  • 谢谢,@AndriyTylychko,我也必须将其包装在 std::remove_const_t 中,但它成功了!

标签: c++ c++11 typeof decltype


【解决方案1】:

使用type alias

class Foo
{
    public:
        using array_t =          std::array<double, 9>; 
    private:
        array_t                  foo;
    public:
        const array_t  &         getFoo() const { return foo; }
        void                     setFoo(const array_t & _foo) { foo = _foo; }
};

允许您将类型提供给用户并且允许您不必输入std::array&lt;double, 9&gt;。您还可以获得仅在一处更改类型的好处。

在外部代码中,您可以声明类成员类型的变量,例如

Foo::array_t bar;

【讨论】:

  • typedef 一样,这实现了我想要的——但要付出额外的代价来声明这些类型。为什么我不能引用(方法和/或字段的)现有声明?
  • 我知道我会从你那里找到另一个值得支持的可靠答案!不过,我不喜欢 OP 中的所有水平间距以及扩展代码。项目的物理位置应该以它们应该被阅读的方式放置,而不是它看起来的审美方式(谢谢,Bob Martin)
  • @MikhailT。上课很奇怪。在定义类之前,内联函数定义是不完整的。鉴于在类范围中添加类型别名并使用它然后尝试使用 decltype (至少我认为是这样)更容易。您可以将类型提供给用户也很好。如果您不希望用户知道内部类型,那么您可以将别名设为私有。
  • @AndyG -- 甚至不在课堂之外的代码中?
  • @user463035818 只要名称的范围正确,就不是问题。在这种情况下,它的范围仅限于类,因此它不会与 POSIX 类型冲突。这也是为什么几乎所有用户代码都应该在命名空间中定义的另一个原因,特别是如果您正在编写一个其他人将使用的库。
【解决方案2】:

您可以将decltype 与类型修饰符一起使用:

std::decay_t<decltype(instance.getFoo())> values; // std::array<double, 9>
for (auto &i : values)
    i = something();
instance.setFoo(values);

【讨论】:

  • decay_t 甚至比结合删除 const 和删除引用“更好”。谢谢!
  • 艰难地发现,decay_t 是 C++14 的东西。适用于 g++-8.x,但不适用于 4.8.x...
  • @MikhailT.: /*typename*/ std::decay&lt;T&gt;::type 在 C++11 中。
【解决方案3】:

您的代码几乎没有上下文,但通常您只会根据其含义命名事物,例如:

struct my_image {
    typedef std::array<int,900> raw_image_t;
    const raw_image_t& get_raw(){ return data;}
private:
    raw_image_t data;
};

现在用户可以写了

my_image::raw_image_t x = f.get_raw();

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-24
    • 1970-01-01
    相关资源
    最近更新 更多