user7610
user7610

Reputation: 28929

How do I statically check my templated class at definition time?

In C# or Java, the following does not compile, because I "forgot" the where part in class declaration, that specifies that T is instance of something that defines the add method.

class C<T> {
    T make(T t) {
        return t.add(t);
    }
}

I'd like to get similar compile-time check in C++, if I specify incomplete requires for a template argument.

template <typename T>
requires true
class C {
public:
  T make() {
    return T{};
  }
};

I'd like to get a compile error for the C++ code above, stating that method c.make relies on T being default-constructible, which is however not captured in the requires constraints on T.

Can I get the compiler to check that my set of requires constraints is sufficient to cover everything the class implementation does?

I am using gcc (GCC) 10.0.0 20191207 (experimental) for this.

Upvotes: 5

Views: 302

Answers (2)

Sergey Kolesnik
Sergey Kolesnik

Reputation: 3678

template <class T>
requires std::is_default_constructible_v<T>
class C
{
    static_assert(std::is_default_constructible_v<T>, 
        "T is not default-constructible");
};

struct valid
{

};

class invalid
{
    invalid() = delete;
};

int main()
{
    C<valid>();
    // C<invalid>(); // assertion fails.
}

You can write static_assert anywhere inside the class definition, alongside with requires. That will give you an error message you want.


UPDATE After reading the link you have provided, I suppose you just need multiple checks.

You can write a traits struct:


// SFINAE to check if has "add"
template <class T, class = std::void_t<>>
struct has_method_add
{
    constexpr static bool value = false;
};

template <class T>
struct has_method_add<T, std::void_t<decltype(&T::add)>>
{
    constexpr static bool value = true;
};

template <class T, class = std::void_t<>>
struct has_operator_remainder
{
    constexpr static bool value = false;
};

template <class T>
struct has_operator_remainder<T, std::void_t<decltype(&T::operator%=)>>
{
    constexpr static bool value = true;
};

template <class T>
struct error_missing_add
{
    constexpr static bool value = has_method_add<T>::value;
    static_assert(has_method_add<T>::value, "T::add is not defined");
};

template <class T>
struct error_missing_remainder
{
    constexpr static bool value = has_operator_remainder<T>::value;
    static_assert(has_operator_remainder<T>::value, "T::operator%= is not defined");
};

template <class T>
class C
{
    static_assert(std::conjunction_v<error_missing_add<T>, error_missing_remainder<T>>);
    // impl...
};

struct valid
{
    void add();
    int operator%=(int) const;
};

struct missing_add
{
    int operator%=(int) const;
};

struct missing_remainder
{
    void add();
};

int main()
{
    C<valid>{};

    C<missing_add>{}; // error: T::add is not defined
    C<missing_remainder>{}; // error: T::operator%= is not defined

    return 0;
}

Upvotes: 0

user7610
user7610

Reputation: 28929

What I want is called definition checking, and apparently it is not possible currently

C++2a concepts (formerly known as “Concepts Lite” and/or the Concepts TS) famously do not support “definition checking.” The idea of definition checking is that the programmer might write [...]

https://quuxplusone.github.io/blog/2019/07/22/definition-checking-with-if-constexpr/

8.2 Definition checking

Concepts currently do not prevent a template from using operations that are not specified in the requirements. Consider:

template<Number N>
  void algo(vector<N>& v){
    for (auto& x : v) x%=2;
  }

Our Number concept does not require %=, so whether a call of algo succeeds will depend not just on what is checked by the concept, but on the actual properties of the argument type: does the argument type have %=? If not, we get a late (instantiation time) error.

Some consider this a serious error. I don’t [...]

http://www.w.stroustrup.com/good_concepts.pdf

The current proposal checks interfaces and that's where the main benefits for users are, but not template definitions. That has been explicit from the start.

https://isocpp.org/blog/2016/02/a-bit-of-background-for-concepts-and-cpp17-bjarne-stroustrup

Upvotes: 2

Related Questions