c++ 成员函数的派生自动返回类型的类型别名

n7taea2i  于 2023-05-20  发布在  其他
关注(0)|答案(3)|浏览(114)

是否有一种方法,从类定义中推导出成员函数的auto返回类型,并将其与using声明一起使用?例如,我想要:

#include <vector>

class Foo {
  public:
    Foo() = default;
    auto begin() const { return v_.begin(); }
    auto end() const { return v_.end(); }

  private:
    std::vector<int> v_;
  public:
    using iterator = decltype(std::declval<Foo>().begin());
};

这当然会给编译器带来错误

error: use of 'auto Foo::begin() const' before deduction of 'auto'
   15 |     using iterator = decltype(std::declval<Foo>().begin());
      |                               ~~~~~~~~~~~~~~~~~~~~~~~~~^~

因为假定auto直到类型Foo完成之后才被推导。然而,原则上,编译器在using iterator = ...点上确实有足够的信息来推断类型。
可以用template<typename=void> using iterator = ...替换using iterator = ...,但这意味着必须使用Foo::iterator<>而不是Foo::iterator类型,这对Container不起作用。

nmpmafwu

nmpmafwu1#

您可以使用与用于推导方法的返回类型相同的表达式,即declval(v_.begin())

struct Foo {
    std::vector<int> v_;
    auto begin() const { return v_.begin(); }
    using iterator = decltype(v_.begin());
};

或尾随返回类型:

struct Bar {
    std::vector<int> v_;
    auto begin() const -> decltype(v_.begin()) { return v_.begin(); }
    using iterator = decltype(std::declval<Bar>().begin());
};

PS:不要写一个什么都不做的构造函数。如果你想声明它,那么就把它设为Foo() = default;

jc3wubiy

jc3wubiy2#

在这里我们被If the type of an entity with an undeduced placeholder type is needed to determine the type of an expression, the program is ill-formed.规则所束缚
类成员的返回类型的推导直到类本身可以完成才完成。除了463035818_is_not_a_number所写的,在某些情况下,我们可能会交错演绎(并将实现与接口分离)。对最简单的类没有意义,但对CRTP很有用。

class FooProps {
private:
    std::vector<int> v_;
public:
    auto begin() const { return v_.begin(); }
    auto end() const { return v_.end(); }
};

class Foo : public FooProps { 
    using iterator = decltype(std::declval<FooProps>().begin());
};

反过来,FooProps可能是一个mix-in,然后容器的定义转到trait类。

yiytaume

yiytaume3#

然而,原则上,编译器在使用iterator = ...时似乎有足够的信息。来推断类型。
你可能会这么想,但事后提供额外的信息可以改变答案:

// ...
    using iterator = decltype(std::declval<Foo>().begin());

    auto begin() { return v_.begin(); }
    auto end() { return v_.end(); }
};

通过添加begin的非const重载,我们将iterator的类型从std::vector<int>::const_iterator更改为std::vector<int>::iterator
相反,写一些不需要Foo完整的东西,例如:

using iterator = decltype(std::as_const(v_).begin());

相关问题