Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Should I repeat concept constraints when using another constrained template?

I want to define a class template Bar<T>, which uses another class template Foo<U>, and the template parameter T is passed as the template argument of Foo<U> somewhere.

Then if U in Foo<U> has some concept constraits (std::floating_point for example), should I repeat the constrait on T when defining Bar?

#include <concepts>

template <std::floating_point U>
struct Foo { /* ... */ };

// <typename T> or <std::floating_point T>?
template <typename T>
struct Bar {
    void Func() {
        Foo<T> foo;  // Uses Foo here
        /* ... */
    }
};

<typename T> and <std::floating_point T>, what are the benefits and tradeoffs of each version?

like image 936
Timothy Liu Avatar asked Apr 28 '26 05:04

Timothy Liu


2 Answers

Specifying the same constraint in two places is both duplication and a form of coupling.

Therefore you should only do this if it actually fixes or improves something.

You're not using SFINAE and you'll get a reasonable error message when the instantiation of Foo fails anyway, in this case there's no benefit, and some disbenefit. Don't do it.

like image 54
Useless Avatar answered Apr 30 '26 18:04

Useless


I would say you have 2 options to be SFINAE friendly,

Either constraint the whole class:

template <std::floating_point U>
struct Foo { /* ... */ };

template <std::floating_point T>
struct Bar {
    void Func() {
        Foo<T> foo;  // Uses Foo here
        /* ... */
    }
};

or, constraint only the method:

template <std::floating_point U>
struct Foo { /* ... */ };

template <typename T>
struct Bar {
    void Func() requires(std::floating_point<T>) {
        Foo<T> foo;  // Uses Foo here
        /* ... */
    }
};

Which one is the most adapted would depend of the purpose of the classes/methods

like image 30
Jarod42 Avatar answered Apr 30 '26 20:04

Jarod42