为什么我在使用 std::enable_if [duplicate] 时出现过载错误

Why do I get overload error while using std::enable_if [duplicate]

提问人:Yves 提问时间:7/1/2023 最后编辑:Yves 更新时间:7/2/2023 访问量:89

问:

我想根据类的一个模板参数定义两种成员函数。这是我的代码:run

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_;
};

如您所见,我正在尝试用不同的 .runIS_DAILY

但是我得到了一个错误:

<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) {

我现在很困惑......不是这样用的吗?std::enable_if

顺便说一句,我正在使用 C++14。

C++ 模板 C++14 SFINAE enable-if

评论

0赞 Patrick Roberts 7/1/2023
要么做两个基于值的部分专用化,要么使用 实现一个,或者使用 C++20 你可以使用 和TimerJobIS_DAILYrunif constexprrequires IS_DAILYrequires (!IS_DAILY)
0赞 Patrick Roberts 7/1/2023
仅供参考,仅当条件依赖于方法模板的模板参数而不是类模板时,重载才有效。std::enable_if_t
0赞 Yves 7/1/2023
@PatrickRoberts 但也不会起作用。 是方法的模板参数,对吧?template <typename C, typename F, typename... Args, bool IS_DAILY_FUNC = IS_DAILY, typename = std::enable_if_t<IS_DAILY_FUNC>>IS_DAILY_FUNC
1赞 ALX23z 7/1/2023
@Yves你需要将帕特里克所说的话与alfC的不完美答案结合起来。
0赞 Patrick Roberts 7/1/2023
是的,显然,如果您使用方法模板间接 + NTTP,它会起作用:godbolt.org/z/he8f18va8 我建议使用部分专业化来定义 CRTP mixin,这完全回避了对 的需求,但它有点复杂: godbolt.org/z/3qTar3EKostd::enable_if

答:

3赞 alfC 7/1/2023 #1

因为(模板)签名完全相同。

一种常见的解决方法是使用非类型参数来解决此问题。 在任何情况下,您始终需要使 enable_if 的内容依赖于至少一个当前参数。

试试这个:

  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) {
...
  }

我认为这就是它的工作原理。

代码对比:https://godbolt.org/z/b5v13MaWf(取消注释两行末尾的代码,查看原始问题)

注意:在 C++ 14 中,缺少 ,您应该为此使用标记调度,而不是 。像这样:https://godbolt.org/z/KhW76Wbszif constexprstd::enable_if

评论

1赞 Yves 7/1/2023
嗯,但会产生另一个错误:TimerJob<30, std::chrono::seconds, false> job;error: no type named 'type' in 'struct std::enable_if<false, void>' 2610 | using enable_if_t = typename enable_if<_Cond, _Tp>::type;
0赞 alfC 7/1/2023
真?godbolt.org/z/WMe71GnbK
0赞 Patrick Roberts 7/1/2023
@alfC真的。godbolt.org/z/q5dh1K5W3
0赞 alfC 7/1/2023
我明白你的意思了,在这种情况下,你需要做一个额外的步骤:godbolt.org/z/b5v13MaWf
1赞 alfC 7/1/2023
的参数 always 必须是依赖于前面模板参数的表达式。这是使其正常工作的唯一方法(否则它会产生硬错误,而不是 SFINAE)。要使表达式依赖于当前模板参数,我必须添加一个不影响结果的虚假依赖项,因为始终为 true,我可以添加 .我通过Raymond Chen的推荐来进一步确保这一点,因为将来我们可能会有零大小的类,但永远不会有指针。我仍然建议使用标签调度enable_ifsizeof(anything)and true
-2赞 MURUGESAN N 7/1/2023 #2

我尝试使用您的代码作为基本版本:

/*
    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_;
};