1
votes

I am trying to use C++11 to define a class which can store several std::function<> and call them depending on their argument types, similar to overload resolution.

I declare a base class for each function signature the 'overloaded' function should support:

template <typename R, typename... A>
struct overload;

template <typename R, typename... A>
struct overload<R(A...)>
{
  typedef std::function<R(A...)> F;
  F f_;
  overload(F f): f_(f) {}
  R operator()(A... a)
  {
    return f_(a...);
  }
};

template <typename... T>
struct overloaded_function : public overload<T>...
{
  overloaded_function(T... t): overload<T>(t)... {}
};

int main()
{
  overloaded_function<void(float), void(int, int)> f([](float){}, [](int, int){});
  f(1.F);  // build error
  f(2, 3); // build error
  return 0;
}

Build error: (Visual Studio 2013)

"overload::operator() [with R=void, A=]" is ambiguous c:\Users\benj7280\Documents\kernel_builder\src\main.cpp 39 5 kernel_builder

I don't understand how the operator can be ambiguous since the functions have totally different signatures. The error was exactly the same when I removed the templates altogether, using only concrete classes, and also when I replaced the operator() overload with a named member function.

1
Member functions with the same name from different base classes don't overload. You need usings. - T.C.
@T.C. That edit might hide a misunderstanding of the OP.. - dyp
@dyp Fair enough. I'll just note in the comment that the primary template for overload should just have one parameter with no pack. - T.C.
@Kietz Workaround: rextester.com/JPDXKV81915 Probably related to using this in a trailing-return-type, I guess. - dyp

1 Answers

1
votes

You need to include the base class operator()s with using. However, I don't know how to do that with parameter packs so hopefully somebody else can hop in and answer that.

What I can do is redo your structure so that it inherits linearly all the way up and each overload only needs to using the next one:

template <typename... T>
struct overloads
{
    // need this one so that the last overload has 
    // something to "using". If you have an overload for void(),
    // that one will hide this one. If you don't, and call f(),
    // this won't compile... which is fine.
    void operator()();
};

template <typename R, typename... A, typename... T>
struct overloads<R(A...), T...>
: overloads<T...>
{
  typedef std::function<R(A...)> F;
  F f_;

  template <typename... Fs>
  overloads(F f, Fs... fs)
  : overloads<T...>(fs...)
  , f_(f)
  {}

  using overloads<T...>::operator();

  R operator()(A... a)
  {
    return f_(a...);
  }
};

template <typename... T>
struct overloaded_function : public overloads<T...>
{
  overloaded_function(T... t)
  : overloads<T...>(t...)
  { }
};

And now your f(1.F) and f(2, 3) calls both compile, since now all the overloads are... overloadable.