3

In java we can specify the type of the of the parameter

public <T extends SomeInterface> void genericMethod(Set<? extends T> tSet) {
    // Do something 
}

It is written as T extends SomeInterface. Is this feature supported by c++?

Jonas
  • 6,641
  • 8
  • 29
  • 50
Silly square
  • 57
  • 1
  • 4

2 Answers2

4

It sounds to me like you want something like this:

template <class T>
std::enable_if_t<std::is_base_of<SomeInterface, T>::value, void>
genericMethod(std::set<T> tSet)
{
    // Do something
}

If you can elaborate on what Set<? extends T> tSet means, then I'm sure we can incorporate that as well.

Jonas
  • 6,641
  • 8
  • 29
  • 50
  • Thx ,But i know that java supports the type check in the parameter.I am asking if this feature supported by c++. – Silly square Jun 30 '17 at 11:24
  • @Sillysquare Sorry, my Java skills are at best very rusty. So does it mean that `?` should be a type that then extends `T`? – Jonas Jun 30 '17 at 11:26
  • 1
    @Jonas Looking at [this](https://stackoverflow.com/questions/4343202/difference-between-super-t-and-extends-t-in-java) it looks like what you have does exactly what the OP wants. My only doubt is in `Set extends T> tSet)` is `?` allowed to be `SomeInterface`. If it is you're fine, if it is not then `std::enable_if_t::value && !std::is_same::value, void>` should work. – NathanOliver Jun 30 '17 at 11:39
  • I'm curious, why use SFINAE for this at all and complicate things? Why not just have a base pointer as the argument to the function? I'm just wondering why one would prefer this over base pointers, is the purpose to allow forwarding and copies? If so java has and does neither implicitly – Curious Jun 30 '17 at 11:48
  • @Curious One reason could be to avoid RTTI and virtual functions. – Jonas Jun 30 '17 at 11:55
  • @Jonas but OP is asking the question asking for a java equivalent right? – Curious Jun 30 '17 at 11:58
  • @Jonas Using an instance of a derived class in contexts expecting an instance of its base class does not require RTTI or virtual functions at all. – underscore_d Jun 30 '17 at 22:04
  • @Curious You can't "use a base pointer" - or reference, because those are polymorphic too - because the type that may vary is the element type of a container. If `T` is a base of `U`, then `foo` is an unrelated type to `foo`, so we can't write a function taking `foo&` and pass a `foo&` to it. But a combination of templates and traits can ensure that functions are generated to handle either type, but no others. This answer is better because it actually addresses the pattern in the OP, not an easier question that they didn't ask. – underscore_d Jun 30 '17 at 22:11
1

You can do this one of two ways, the simplest solution here is to use a base class pointer that represents the interface. After that point you can only pass pointers to objects that are derived from that base class. So something like this

#include <iostream>
#include <string>

using std::cout;
using std::endl;

class BaseOne {};
class BaseTwo {};

class DerivedOne : public BaseOne {};
class DerivedTwo : public BaseTwo {};

void foo(BaseOne*) {
    cout << __PRETTY_FUNCTION__ << endl;
}
void foo(BaseTwo*) {
    cout << __PRETTY_FUNCTION__ << endl;
}

int main() {
    auto derived_one = DerivedOne{};
    auto derived_two = DerivedTwo{};
    foo(&derived_one);
    foo(&derived_two);
}

Or if the goal is to do this at compile time without base classes, i.e. without inheritance and without concepts (which are expected to come out in C++20 ¯\_(ツ)_/¯) and only check the presence of some methods, then you can do something like this

#include <iostream>
#include <type_traits>

using std::cout;
using std::endl;

struct One { void one() {} };

struct Two { void two() {} };

/**
 * Loose SFINAE based concepts
 */
template <typename Type, typename T = std::decay_t<Type>>
using EnableIfOne = std::enable_if_t<std::is_same<
        decltype(std::declval<T>().one()), decltype(std::declval<T>().one())>
    ::value>;
template <typename Type, typename T = std::decay_t<Type>>
using EnableIfTwo = std::enable_if_t<std::is_same<
        decltype(std::declval<T>().two()), decltype(std::declval<T>().two())>
    ::value>;

template <typename T, EnableIfOne<T>* = nullptr>
void foo(T&) {
    cout << __PRETTY_FUNCTION__ << endl;
}
template <typename T, EnableIfTwo<T>* = nullptr>
void foo(T&) {
    cout << __PRETTY_FUNCTION__ << endl;
}

int main() {
    auto one = One{};
    auto two = Two{};
    foo(one);
    foo(two);
}
Curious
  • 19,352
  • 6
  • 45
  • 114
  • Can i ask you one question? How many minutes did that took from you to write? – Silly square Jun 30 '17 at 11:25
  • @Sillysquare the first one like 2 minutes, the second one like 3. But this is because I knew beforehand how to use `enable_if` and write functions like the second – Curious Jun 30 '17 at 11:29
  • @Sillysquare although it seems like from what you want the first solution will work just fine – Curious Jun 30 '17 at 11:32