MoonBun
MoonBun

Reputation: 4402

c++ return template function

I have functions of this type:

type uniRndtype()
{
    return typeValue;
}

and now I'm trying to wrap them inside another template function like this:

template<typename T>
T(* uniRndType(void))()
{
    if (is_same<T, bool>::value)
    {
        return uniRndBool;
    } else if (is_same<T, char>::value)
    {
        return uniRndChar;
    } else
    ...
}

and calling it like this:

uniRndType<int>();

But I'm getting an error: "error: return value type does not match the function type" because each return has a different type..

I there a way to make it work? Because from a runtime point of view I see no errors, only the compiler have problems.

Upvotes: 1

Views: 4955

Answers (4)

ikh
ikh

Reputation: 10417

template <typename T> T (*uniRndType())()
{
    //else
    ...
}

template <> bool (*uniRndType())()
{
    return uniRndBool;
}

template <> char (*uniRndType())()
{
    return uniRndChar;
}

That's all.

edit: In principle, we must do like @Angew. but it's little troublesome

Upvotes: 1

VP.
VP.

Reputation: 16685

Compiler needs to know what types will be used by functions/methods at compile time. At this point you must instantiate this template function with types which it may receive as template parameter, like @ikh wrote here.

But, if you are using template class with template methods, you have 2 ways:

1) Simply write realization of each template method in .h file (right in the place where you declaring template class body) instead of prototypes only;

2) Or after declaring prototypes in .h file you need to instatiate this template class with template parameters which it may receive.

Upvotes: 0

The problem is that while the optimiser can eliminate dead code branches, the front-end (lexical, syntactic & semantic analysis) can't. Which means all code in a template instantiation must be valid. That is, even though in this:

if (is_same<T, bool>::value)
{
    return uniRndBool;
}

the body will never be executed when T is char, it must still be valid C++ code. And of course it's not, because uniRndBool doesn't have the correct type.

You have two options: a hackish one which works in your particular case, and a generic one.

The hackish one is using reinterpret_cast<T(*)()> in all the return statements. For the correct T brach, it will be a no-op. The other branches will never be executed at runtime, so all will be fine.

The other solution is to use template specialisation. Since it's a bad idea to specialise function templates, you could use the well-known "delegate to class" trick:

template <class T>
struct uniRndTypeHelper;

template <>
struct uniRndTypeHelper<bool>
{
  static bool (*get())() { return uniRndBool; }
};

template <>
struct uniRndTypeHelper<char>
{
  static char (*get())() { return uniRndChar; }
};

template<typename T>
T(* uniRndType(void))()
{
  return uniRndTypeHelper<T>::get();
}

Upvotes: 3

Sean
Sean

Reputation: 62472

This won't work because when the compiler expands the template method for a given type the method has to be valid. Your type, T, needs to be compatible with all the return types listed, eg bool, char, etc. As I mentioned in the comments, if you passed in a std::string you wouldn't expect the method to work, would you?

One way around this is to use template specialization to indicate what you want for each type.

Upvotes: 0

Related Questions