Andrew Spott
Andrew Spott

Reputation: 3627

template with duck typing in C++

Is there a way to require a templates type to have properties?

For example:

template <typename T, typename U>
void foo()
{
    U a,b;
    bool truthiness = T()(a,b);
    if (truthiness)
        // do something
}

So, how would I require that T have the operator()(U a, U b) defined, that returns a specific type? Is this possible? (I know it is in d, but I'm not sure about c++).

ps. if duck typing is wrong here, let me know, I believe it is correct, but I'm not sure.

Upvotes: 0

Views: 720

Answers (3)

Bowie Owens
Bowie Owens

Reputation: 2976

If you have access to decltype, you can roll your own check relatively easily.

template <class T, class U> class check_same_type_t;

template <class T> class check_same_type_t<T, T> { };

template <class T, class U>
void foo()
{
    U a,b;
    check_same_type_t<bool, decltype(T()(a, b))> check;
    bool truthiness = T()(a,b);
    if (truthiness) ;
        // do something
}

Which enables the following:

struct A {
    bool operator()(int, int) { return true; }
};

struct B {
    int operator()(int, int) { return 1; }
};

int
main()
{
    foo<A, int>(); // will compile
    foo<B, int>(); // won't compile

}

Just make sure that this what you really want. Forcing a generic algorithm to use a specific type may potentially come back to bite you. If a type is implicitly convertible to bool, why is it unsatisfactory to use in your condition?

Upvotes: 0

AnT stands with Russia
AnT stands with Russia

Reputation: 320481

Your syntax is wrong, considering your intent. Since T is a type, T(1, 2) would construct a temporary object of type T using a two-parameter constructor. If you wanted to call Ts operator () you'd have to вo something like

T()(1, 2);

assuming a call through a temporary works for your purposes.

If T had no such operator (), the code would simply fail to compile. I'd actually say that one big benefit of template code is that it "works" as long as the syntax is valid (i.e. the very duck-typing you are talking about), i.e. there's no need to further restrict it by requiring the operator () be present.

Of course, in my example it might actually make sense, since for T = void (*)(int, int) the code would be syntactically valid, but would result in a function call through a null pointer. But again, that's specific to my version of the code, and I don't know what specific object of type T you want to apply your operator () to.

Having said that, it is worth nothing that Boost library has quite a few features that allows one to check such properties and use them for branching in template meta-programming and/or in static assertions.

Upvotes: 4

111111
111111

Reputation: 16148

By simple expressing the template then you require for T to have operator()(int, int). It will not compile if it doesn't.

However if you are creating an API and want to inform the user of the API that they have passed in an incompatible type then you need to create a type trait which detects the operator and you can specialise the function to discriminate on that fact and create a static_assert to indicate the fact.

Upvotes: 2

Related Questions