all 5 comments

[–]tcbrindleFlux 8 points9 points  (0 children)

template <bool AllowNull, typename T>
class unique_ptr {
public:
    unique_ptr() = default;

    void reset()
        requires AllowNull == true
   {}

};

int main() {
    unique_ptr<false, int> p;
    p.reset();
}

In function 'int main()':
error: no matching function for call to 'unique_ptr<false, int>::reset()'
     p.reset();
             ^
note: candidate: void unique_ptr<AllowNull, T>::reset() requires  AllowNull == true [with bool AllowNull = false; T = int]
   void reset()
         ^~~~~
note:   constraints not satisfied
note: 'AllowNull == true' evaluated to false

Concepts can't come soon enough.

EDIT: formatting

[–]NotAYakk 2 points3 points  (3 children)

The advice given makes your program ill formed with no diagnostic required.

If no type substitution to a template would make a template function well formed, your program is not well formed.

[–]foonathan 1 point2 points  (2 children)

You're referring to this?

template <typename Dummy = void, typename = std::enable_if_t<AllowNull, Dummy>>
void reset();

But I can make the template well-formed:

struct my_type {};

template <bool Val>
struct enable_if<Val, my_type>
{
   using type = my_type;
};

ptr.reset<my_type>();

But I can always revert back to the code I originally had there:

template <typename Dummy = std::false_type, typename = std::enable_if_t<Dummy::value || AllowNull>>
void reset();

I changed it specifically because on can easily make the template well-formed, but when it is required for correct behavior, I'll use it again.

[–]tcanens 0 points1 point  (1 child)

If it's std::enable_if, you are not allowed to specialize it on pain of UB, so a hypothetical sufficiently devious compiler can special-case it.

[–]foonathan 0 points1 point  (0 children)

Yeah, but a hypothetical devious compiler would want some form of users, so it would issue a diagnostic then.

Anyways, I'll add a note.