Why do I get overload error while using std::enable_if

94 Views Asked by At

I want to define two kinds of member functions run according to one template paramter of class. Here is my code:

template <int VAL, typename D = std::chrono::seconds, bool IS_DAILY = false>
class TimerJob {
public:
  template <typename C, typename F, typename... Args, typename = std::enable_if_t<IS_DAILY>>
  void run(F C::*f, C* c, Args&&... args) {
    td_ = std::make_unique<std::thread>(
        [](F C::*f, C* c, Args... args){
            do {
              c.f(args...);
              std::this_thread::sleep_for(D{VAL});
            } while (true);
        });
  }

  template <typename C, typename F, typename... Args, typename = std::enable_if_t<!IS_DAILY>>
  void run(F C::*f, C* c, Args&&... args) {
    td_ = std::make_unique<std::thread>(
        [](F C::*f, C* c, Args... args){
            do {
              c.f(args...);
              // just an example to make a difference with the other run()
              // the real case is much complicated
              std::this_thread::sleep_for(D{VAL + 60});
            } while (true);
        });
  }
private:
  std::unique_ptr<std::thread> td_;
};

As you see, I'm trying to overload the function run with different IS_DAILY.

But I got an error:

<source>:27:8: error: 'template<int VAL, class D, bool IS_DAILY> template<class C, class F, class ... Args, class> void TimerJob<VAL, D, IS_DAILY>::run(F C::*, C*, Args&& ...)' cannot be overloaded with 'template<int VAL, class D, bool IS_DAILY> template<class C, class F, class ... Args, class> void TimerJob<VAL, D, IS_DAILY>::run(F C::*, C*, Args&& ...)'
   27 |   void run(F C::*f, C* c, Args&&... args) {
      |        ^~~
<source>:15:8: note: previous declaration 'template<int VAL, class D, bool IS_DAILY> template<class C, class F, class ... Args, class> void TimerJob<VAL, D, IS_DAILY>::run(F C::*, C*, Args&& ...)'
   15 |   void run(F C::*f, C* c, Args&&... args) {

I'm confused now... Isn't std::enable_if used like this?

BTW, I'm working with C++14.

2

There are 2 best solutions below

9
alfC On BEST ANSWER

Because the (template) signatures are exactly the same.

A common workaround is to use a non-type parameter to solve this. And in any case, you always need to make the contents of enable_if dependent on at least one of the current parameter.

Try with this:

  template <typename C, typename F, typename... Args, std::enable_if_t<IS_DAILY and sizeof(C*)>* =nullptr> // typename = std::enable_if_t<IS_DAILY>>

...
  }

  template <typename C, typename F, typename... Args, std::enable_if_t<!IS_DAILY and sizeof(C*)>* =nullptr> // typename = std::enable_if_t<!IS_DAILY>>
  void run(F C::*f, C* c, Args&&... args) {
...
  }

I think this is how it works.

Code comparison: https://godbolt.org/z/b5v13MaWf (uncomment the code at the end of the two lines to see the original problem)

NOTE: In C++14, lacking if constexpr, you should use tag dispatch for this, not std::enable_if. Like this: https://godbolt.org/z/KhW76Wbsz

0
MURUGESAN N On

I tried following using your code as base version:

/*
    Compilation using MINGW at Windows:
    /usr/bin/g++.exe -g -c -Wall 76593060.cpp -std=c++17
    $ /usr/bin/ls -ltr 76593060.o
    -rw----r--+ 1 murugesan openssl 19360 Jul  1 09:02 76593060.o
*/
#include <thread>   // ‘std::thread’ is defined in header ‘<thread>’;
#include <memory>   // ‘std::unique_ptr’ is defined in header ‘<memory>’
#include <type_traits>  // std::enable_if_t
#include <chrono>   // std::chrono::seconds
#include <iostream>
using namespace std;    // Remove all std::
template <int VAL, typename D = chrono::seconds, bool IS_DAILY = false>
class TimerJob
{
    public:
    template <typename C, typename F, typename... Args, typename = enable_if_t<IS_DAILY>>
    void run(F C::*f, C* c, Args&&... args)
    {
        td_ = make_unique<thread>(
        [](F C::*f, C* c, Args... args){
        do
        {
            c.f(args...);
            this_thread::sleep_for(D{VAL});
        } while (true);
        });
    }

    // REQUIREMENT VIEWED AT ERROR DURING COMPILATION:
    // template<class C, class F, class ... Args, class>
    // HOWEVER OLD CODE WAS NOT HAVING FOURTH ARGUMENT:
    template <typename C, typename F, typename... Args, typename = enable_if_t<!IS_DAILY>>
    // void run(F C::*f, C* c, Args&&... args)
    void run(F C::*f, C* c, Args&&... args, bool DAILY_VALIDATION)
    {
        td_ = make_unique<thread>(
        [](F C::*f, C* c, Args... args){
        do
        {
            c.f(args...);
            // just an example to make a difference with the other run()
            // the real case is much complicated
            this_thread::sleep_for(D{VAL + 60});
        } while (true);
        });
    }
    private:
    unique_ptr<thread> td_;
};