Bounded Type Parameters in C++, any reason for the lack of it?

1.5k Views Asked by At

Java and I guess C#(and others) support Bounded Type Parameters which lets us restrict what types may be used in template classes/functions.

I am curious if there is a reason, official or otherwise, for not adding native support for bounded types to C++? Anything to do with how templates are currently processed? Multiple inheritance issues?

I would expect it to be quite useful.

4

There are 4 best solutions below

0
On

The simple fact is, the reason why this is not in is because nobody has come up with a feature that would make it work without horrific side effects. The Committee has been working on the problem for a decade or more, and the latest iteration still isn't fit for purpose.

Also, the generic restrictions you refer to are not bounded types at all. The only bound they support is "X inherits from Y", essentially, and frankly, SFINAE with std::is_base_of covers this situation just fine. C++ would need something far more powerful to be useful, since run-time inheritance is one of the least useful features.

0
On

C++ has SFINAE which can be exploited via std::enable_if fairly easily. In conjunction with type_traits it is actually, IMO, more powerful than the bounded types that Java and C# have. With a little work you can also make some nice constexpr functions to test these things out for you. Combine that with some macros and you have something that looks sorta like it

#include <iostream>
#include <type_traits>

#define ENABLE_IF typename std::enable_if<
#define THEN(T) ,T>::type

class foo {};
class bar : public foo {};

template<class T, class U>
constexpr bool extends() {
    return std::is_base_of<
        typename std::remove_reference<U>::type, 
        typename std::remove_reference<T>::type
    >::value;
}

template<class T>
ENABLE_IF extends<T, foo>() THEN(void) test(T&& v) {
    std::cout << "T extends foo!!";
}

int main() {
    test(bar{});
}

Now I'm not sure I would recommenced this but it is doable and as of now I see no issue in doing it beyond SFINAE being hard to debug

3
On

Most of the time, the constraints on a template argument should not be on the type, but on the operations that the template needs. C++ does that, in a somewhat awkward way, simply because you get an error message if an operation that the template uses isn't there. For example:

template <class T>
void show(T t) {
    std::cout << t << std::endl;
}

If you call this template function with a type that doesn't implement operator<< you'll get an error. The Java approach would be to define an interface with a print method, and require that the user pass an object of a type that implements that interface. The C++ approach doesn't require all that mechanism.

The problem with doing this in C++ is that you can get error messages that are very confusing. Often the missing operation is used in some low-level part of another template, and the error message has no clear relation to the code that you wrote. That's one of the drives behind concepts: the author of the template can set out what operations it uses, and passing an object whose type doesn't support those operations will result in a violation of the concept right at the interface, instead of deep within the implementation, so you will probably get a more useful error message.

0
On

The purpose of the bounded type parameter is to raise a compile-time error in case of a mismatch of the supplied type and a desired base-class, so this is easily achievable in C++11 and up, with a static_assert and supplying to it the value of the std::is_base_of as follows:

template <typename T>
class C {
    static_assert(std::is_base_of<SomeBoundedBaseClass, T>::value, "Bounded type parameter violation!");
    //...the rest of the class C
};

where SomeBoundedBaseClass is your class to which you want to bound the type parameter T to be a descendant of or match exactly.

Also note that this way you can mention any custom message to be shown as a compile error, so it has even an advantage over the Java's built-in functionality. Needless to say that C++ is more verbose, but it gives also more freedom.