SwiftMango
SwiftMango

Reputation: 15294

How to enable a function only if the parameter is dereferencable to a certain type?

I am making a function template:

template <typename T>
void foo (T arg1) {}

But I want to make sure T is deferencable to type Foo, so arg1 needs to be either:

Foo * //This case T = Foo*

Or

Any type X which implements `*` operator which returns a Foo/Foo&/Foo&&

So I need something like:

template <typename T>
void foo(T arg1, std::enable_if<std::is_same<typeid(*arg1), typeid(Foo)>::value> * = 0) {}

But this does not compile and complains:

typecheck.cpp:6:54: error: use of parameter âarg1â outside function body
 void foo(T arg1, std::enable_if<std::is_same<typeid(*arg1), typeid(Foo)>::value> * = 0) {}
                                                      ^

How can I achieve this?

Upvotes: 1

Views: 684

Answers (3)

galop1n
galop1n

Reputation: 8824

You should prefer is_convertible instead of is_same to catch sub classes of Foo, the enable_if in the template argument list instead of the function arguments or return type is also easier to read.

template <typename T, typename = typename std::enable_if<std::is_convertible<decltype( *std::declval<T>() ), Foo>::value>::type >
void foo(T arg1);

less garbage in c++14 :

template <typename T, typename = std::enable_if_t<std::is_convertible<decltype( *std::declval<T>() ), Foo>::value> >
void foo(T arg1);

Upvotes: 7

user2249683
user2249683

Reputation:

A template is way too complicated:

void foo(Foo&);
void foo(const Foo&);
void foo(Foo*);
void foo(const Foo*);

and dispatch accordingly.

#include <iostream>

struct Foo {};
struct Any {
    operator Foo () const { return Foo(); }
};

void foo(const Foo&) {
    std::cout << "Hello Foo\n";
}

int main() {
    Any a;
    foo(a);
}

Upvotes: 0

Joseph Mansfield
Joseph Mansfield

Reputation: 110698

You want decltype, not typeid. You use typeid to get run-time type information, whereas decltype will give you the type of an expression at compile-time.

std::is_same<decltype(*arg1), Foo>::value

Note, if you don't need a call with a type that doesn't point at a Foo to fall back to a different overload, then you might prefer to use static_assert instead of std::enable_if:

template <typename T>
void foo(T arg1) {
  static_assert(std::is_same<decltype(*arg1), Foo>::value, "Performing indirection with an object of type T must give a Foo");
  // ...
}

Upvotes: 3

Related Questions