繁体   English   中英

概念的常量版本作为 requires 子句中的返回类型

[英]Constant version of a concept as return type in requires clause

我有一些A类,它有一些const和一些非const函数,以及一个合适的概念,比如

class A {
public:
    void modify() {/* ... */}
    void print() const {/* ... */}
}

template<typename T>
concept LikeA = requires (T t, const T const_t) {
    { t.modify() } -> std::same_as<void>;
    { const_t.print() } -> std::same_as<void>;
}
static_assert(LikeA<A>);

我注意到的一件好事是,对于某些采用const LikeA auto &a下面代码的函数实际上是合法的:

void use (const LikeA auto &a) {
    a.print(); // fine, print is a const method
    // a.modify(); // illegal, a is a const reference [at least for use(A a) itself, but this is not my point here]
}

static_assert(!LikeA<const A>);

int main() {
    A a1;
    use(a1); //clear, as LikeA<A> holds
    const A a2;
    use(a2); //not obvious, as LikeA<const A> does not hold
}

我查看了cppreference上的定义,我无法真正解释这种行为,我认为它是非法的,尽管从直觉上讲,这确实是我想要的。

现在AHolder我的真实情况:我有一个持有者类AHolder ,它返回const A&作为其方法之一,我想要一个适合这个持有者类的概念,该概念也适用于任何其他持有满足LikeA东西的LikeA ,所以我尝试了:

class AHolder {
public:
    const A& getA() {return _a;}
private:
    const A _a;
};

template<typename T>
concept LikeAHolder = requires (T t) {
    {t.getA() } -> LikeA;
};

static_assert(LikeAHolder<AHolder>); //fails

这失败了,因为const A&根本不满足LikeA ,所以我很乐意将其调整为

template<typename T>
concept LikeAHolder = requires (T t) {
    {t.getA() } -> const LikeA; //invalid syntax
};

static_assert(LikeAHolder<AHolder>);

与示例的精神类似, use方法也接受const A

是否有这样的语法要求t.getA的返回类型满足LikeA同时考虑到返回类型将是const

此外,如何在use(const LikeA auto &a)方法中检查概念,使其表现得像解释的一样?

(我的第一个问题对我来说更重要)


我考虑过的一些可能的解决方案:

  • 返回一个非常量引用。 这会使上面的代码非法,但当然会严重破坏const正确性,因为_a也必须是非const并且用户可以只更改AHolder的私有属性。 这对我来说没有选择。
  • 有两个概念LikeALikeConstA 然后返回类型可以是LikeConstA只需要const方法。 这应该可以工作,但感觉真的很笨拙,而且真的不应该如何使用概念,这也引入了更多最终用户需要的概念,他们不得不费心,等等。
  • LikeA概念中,检查模板化类型T是否为常量(通过std::is_const ),如果是,则不需要非const方法。 这在上面的例子中有效,但有我们现在简单的不良影响
class B {
public:
    void print() const {/* ... */}
}

static_assert(LikeA<const B>);

(当然,对于已经适应的LikeA ),这也只是感觉不对。

  • LikeA的定义中,使用std::remove_referencestd::const_cast丢弃引用/常量,然后检查所需的函数。 首先,我不知道这是否总是适用于更复杂的类型,但即便如此,这现在也会产生不良影响
static_assert(LikeA<const A>);

将是真的,打破(或至少弯曲)概念的语义。

总结并确保您不会误解我的意思,我想有一种方法

  • 确实强制执行const正确性
  • 不使用第二个概念“为最终用户”。 我的意思是,定义辅助概念或使用一些有助于定义上述概念的标准库当然是可以的,但实际上不需要使用ALikeA等。
  • 不是简单地忽略常量类型的非const的需求(正如我所说,这将是编译器聪明没关系,但语义感觉错)
  • 没有将LikeA<const A>定义为真

理想情况下,只有一个功能可以像已经提到的那样工作

template<typename T>
concept LikeAHolder = requires (T t) {
    {t.getA() } -> const LikeA; //invalid syntax
};
{[](const LikeA auto&){}( t.getA() )}

请注意,非const&返回getA将通过它。

我制作了一个进行概念检查的 lambda,然后确保t.getA()通过它。

void use (const LikeA auto &a)

这是简写

template<LikeA A>
void use (const A&a)

当用T const调用时,它推导出A=T而不是A=T const 为什么? 因为它抽象地“更正确”。 具体来说,模板参数类型推导的工作方式有很多规则。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM