【问题标题】:Using a data member of the derived class in a requires clause of the base CRTP class在基 CRTP 类的 requires 子句中使用派生类的数据成员
【发布时间】:2021-04-07 10:09:00
【问题描述】:

我正在尝试通过 CRTP 和 requires 子句来实现一种静态多态性。

我想要实现的是在引用 CRTP 基类的函数中调用可能被覆盖的函数。

我通过以下方法使其与 GCC 10 和 11 一起工作:

#include <iostream>

template<typename T>
class Base
{
public:
  void f() const requires T::IsOverridden
    { static_cast<T const *>(this)->f(); }

  void f() const
    { std::cout << "Fallback f()" << std::endl; }
};

class A : public Base<A>
{
public:
  static constexpr bool IsOverridden = true;

public:
  void f() const
    { std::cout << "Overridden f()" << std::endl; }
};

class B : public Base<B> {};

template<typename T>
void f(Base<T> const &x)
  { x.f(); }

int main()
{
  A const a;
  B const b;

  f(a);
  f(b);

  return 0;
}

但是,Clang 11 不喜欢这段代码:

test.cpp:7:30: error: no member named 'IsOverridden' in 'A'
  void f() const requires T::IsOverridden
                          ~~~^
test.cpp:14:18: note: in instantiation of template class 'Base<A>' requested here
class A : public Base<A>
                 ^
test.cpp:7:30: error: no member named 'IsOverridden' in 'B'
  void f() const requires T::IsOverridden
                          ~~~^
test.cpp:24:18: note: in instantiation of template class 'Base<B>' requested here
class B : public Base<B> {};
                 ^

哪个编译器是正确的?

注意:我使用布尔成员来表示覆盖,因为我希望它与嵌套在模板类中的类一起使用,这是我在这种情况下想出的唯一方法。

【问题讨论】:

  • CRTP 的问题是DerivedBase&lt;Derived&gt; 定义中的不完整类型(通常是完整的inside 成员定义)。 Clang 似乎是对的。
  • 作为替代方案,您可以使用if constexpr Demo
  • @Jarod42 谢谢。 Clang 接受的另一种解决方法:wandbox.org/permlink/Ki8pshYJpmVYzSQx。当类被实例化时,Clang 是否正确地实例化带有尾随 requires 子句的函数?我也测试过MSVC,同意G++。
  • 它不实例化函数,但类可以检查签名和模板函数是否存在非依赖(关于函数模板参数,因为模板类参数现在已修复)的东西。
  • @Jarod42 Clang 错误。请参阅 Richard Smith 的评论:bugs.llvm.org/show_bug.cgi?id=44833#c4。 (感谢 PilarLatiesa 指出这一点)。

标签: c++ c++20 c++-concepts


【解决方案1】:

我将发布我最终使用的内容,以防它对其他人有用。

我不喜欢使用很容易拼错的成员变量来表示函数已被覆盖。

相反,我使用默认的std::false_type NTTP 作为基类函数,使用std::true_type NTTP 作为重载,以便可以使用 requires 表达式检测它们:

#include <iostream>
#include <type_traits>

template<typename T>
class Base
{
public:
  template<std::false_type = std::false_type{}>
    requires requires(T const x) { x.template f<std::true_type{}>(); }
  void f() const
    { static_cast<T const *>(this)->f(); }

  template<std::false_type = std::false_type{}>
  void f() const
    { std::cout << "Fallback f()" << std::endl; }
};

class A : public Base<A>
{
public:
  template<std::true_type = std::true_type{}>
  void f() const
    { std::cout << "Overridden f()" << std::endl; }
};

class B : public Base<B> {};

template<typename T>
void f(Base<T> const &x)
  { x.f(); }

int main()
{
  A const a;
  B const b;

  f(a);
  f(b);

  return 0;
}

GCC 11 接受更简洁的语法 template&lt;std::true_type = {}&gt;,但 GCC 10 和 CLang 12 需要更长的 template&lt;std::true_type = std::true_type{}&gt;

【讨论】:

猜你喜欢
  • 2016-05-21
  • 2015-01-17
  • 2013-02-05
  • 1970-01-01
  • 1970-01-01
  • 2017-05-10
  • 2017-08-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多