3

I am trying to make a template container class and I want it to conform to the "Container" named requirement as best I can. I am looking at this cppreference link and at the bottom it says:

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

I want to add some static asserts in my code such that I never accidentally regress any functionality, and I am looking into adding it inside the class definition. Here is a minimal representation of my code:

#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;
}

However, when trying to compile this (at the moment using g++ 9.4), the compilation fails on the static assert.

Why is this failing?

Are static asserts even meant to be used this way? For example looking at my c++ standard library implementation of std::vector class, I can clearly see they use some static asserts like this (although not for checking that the "Container" requirements are satisfied) Also, any provided answer must be portable for all major compilers (g++, clang++ and msvc)

2
  • gcc 10 gives a better first error message: error: static assertion failed: template argument must be a complete class or an unbounded array Commented Sep 16, 2023 at 22:04
  • @JaMiT It seems that putting the static assert within the constructor seems to solve the issue Commented Sep 16, 2023 at 23:01

2 Answers 2

1

Within the class body, MyContainer is still an incomplete class. This is due to the fact that classes get parsed in two passes:

  1. parse all member declarations (data members, static_assert, etc.)
  2. parse member definitions (member function bodies, etc.)

You could do

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

... outside the class, but that would only apply to a single specialization for the class template.

You could also put the static_assert in some member function of MyContainer. Within a member function, the surrounding class is complete.

Sign up to request clarification or add additional context in comments.

Comments

0

You can use the PImpl Pattern to separate the implementation from the interface class, and then static_assert properties of the implementation class within the interface class. In this way, the implementation class is complete when you call static_assert().

Live on 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;
}

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.