如何强制执行 C++ 命名要求“容器”[复制]

How to enforce the C++ named requirement "Container" [duplicate]

提问人:TheMemeMachine 提问时间:9/17/2023 更新时间:9/17/2023 访问量:80

问:

我正在尝试制作一个模板容器类,我希望它尽可能符合“容器”命名要求。我正在查看这个 cppreference 链接,底部说:

Other requirements
C (Container)
    DefaultConstructible
    CopyConstructible
    EqualityComparable
    Swappable
T (Type)
    CopyInsertable
    EqualityComparable
    Destructible

我想在我的代码中添加一些静态断言,这样我就不会意外地回归任何功能,并且我正在考虑将其添加到类定义中。这是我的代码的最小表示形式:

#include <iostream>
#include <type_traits>

template <typename T>
class MyContainer {
    public:
        MyContainer() = default;

        static_assert(std::is_default_constructible<MyContainer>::value, "MyContainer is not default constructible!");
};

int main() {
    // instantiate the object so that static assert may be evaluated
    MyContainer<int> obj1;

    std::cout << "All checks passed." << std::endl;

    return 0;
}

但是,当尝试编译它时(目前使用 g++ 9.4),编译在静态断言上失败。

为什么会失败?

静态断言甚至应该以这种方式使用吗?例如,查看我的 c++ 标准库 class 实现,我可以清楚地看到他们使用了一些这样的静态断言(尽管不是为了检查是否满足“容器”要求) 此外,提供的任何答案都必须可移植到所有主要编译器(g++、clang++ 和 msvc)std::vector

C++ C++11 类型特征 static-assert

评论

0赞 JaMiT 9/17/2023
GCC 10 给出了更好的第一个错误消息:error: static assertion failed: template argument must be a complete class or an unbounded array
0赞 TheMemeMachine 9/17/2023
@JaMiT 似乎将静态断言放在构造函数中似乎可以解决问题

答:

1赞 Jan Schultke 9/17/2023 #1

在类体中,仍然是一个不完整的类。这是因为类在两次传递中进行解析:MyContainer

  1. 分析所有成员声明(数据成员等)static_assert
  2. 解析成员定义(成员函数体等)

你可以做到

static_assert(std::is_default_constructible<MyContainer<int>>::value, "MyContainer is not default constructible!");

...在类外,但这仅适用于类模板的单个专业化。

你也可以把 的某个成员函数 放进去。在成员函数中,周围的类是完整的。static_assertMyContainer

0赞 wu1meng2 9/17/2023 #2

可以使用 PImpl 模式将实现与接口类分开,然后在接口类中分离实现类的属性。这样,当您调用 时,实现类就完成了static_assertstatic_assert()

在Coliru上直播

#include <experimental/propagate_const>
#include <iostream>
#include <memory>
#include <type_traits>

template <typename T>
class MyContainer {
  class impl;

  static_assert(std::is_default_constructible<MyContainer::impl>::value,
                "MyContainer is not default constructible!");

  std::experimental::propagate_const<std::unique_ptr<impl>> pImpl;
};

template <typename T>
class MyContainer<T>::impl {
 public:
  impl() = default;
};

int main() {
  // instantiate the object so that static assert may be evaluated
  MyContainer<int> obj1;

  std::cout << "All checks passed." << std::endl;

  return 0;
}