【问题标题】:c++: compile time error on function call if condition not satisfied?c ++:如果条件不满足,函数调用上的编译时错误?
【发布时间】:2026-02-17 03:25:01
【问题描述】:

这是我要实现的目标的简化示例。因此,它可能看起来有点傻,但请耐心等待。假设我有

template<int i> 
class Class1{
foo(){cout<<"j is divisible by i, so we will hang out"<<endl;}
}

和一个class2 有一个固定的int j 变量:要么被这样一个int 模板化,要么有一个成员变量。我希望class2 实例只有在满足特定条件时才能调用foo(),在这种情况下,我想确保说(j%i==0)

我能想到的最好的是:

template<int i> 
class Class1{
    static const int intParam=i;
    template<bool true>
    foo(){cout<<"j is divisible by i, so we will hang out"<<endl;}
}

然后类 2 会这样称呼它:

foo<class1::intParam%j>()

这不是很好。有没有更好的方法来做到这一点?我见过'std::enable_if'这有点相关,但我不太确定。

如果您想要更大的图景,这是一种信号/委托代理机制。在系统中,任务对象应该能够被执行者对象提供/请求,前提是它们与任务中指定的角色枚举(int i)匹配。本质上,这应该是基于枚举的设计,没有动态多态性。在 C++ 中有没有更好的方法来做到这一点?

【问题讨论】:

  • 你试过static_assert吗?
  • @krzaq 查看我对建议答案的评论。

标签: c++ templates crtp enable-if static-polymorphism


【解决方案1】:

使用static_assert:

template<int i, int j> 
class Class1
{
  public:
  void foo()
  {
    static_assert(i % j == 0, "Message upon i % j == 0 being false");
    cout<<"j is divisible by i, so we will hang out"<<endl;
  }
};

并称之为例如

Class1<42, 24> f; 
f.foo();

更新到 cmets,只需添加 j 作为 foo() 的额外模板参数然后:

template<int i> 
class Class1
{
  public:
  template<int j>
  void foo()
  {
    static_assert(i % j == 0, "Message upon i % j == 0 being false");
    cout<<"j is divisible by i, so we will hang out"<<endl;
  }
};

int main() 
{
    Class1<42> f; 
    f.foo<24>();
    return 0;
}

【讨论】:

  • 谢谢。但这会给我在 foo() 的主体内的错误,并且在实际点击编译之前我不会点击它。我希望有一些东西可以阻止我在编辑器中开始编写 foo()。
  • 你的例子也偏离了我上面描述的例子。 j 不属于 Class1。
  • @user4376555 这没有多大意义。如果我在记事本中编程怎么办?在我用代码实际编译我的文件之前,没有什么会被视为错误。在编译之前,文本没有任何意义。
  • @user4376555 不,但您可以将j 传递给班级。
  • @user4376555 这仍然需要模板。通常,当模板开始涉及时,“背景语法检查器”就会放弃,让编译器自己决定什么是有效的,什么是无效的。
【解决方案2】:

与其他解决方案相反,我建议使用 SFINAE 根据条件启用禁用功能。

template<int i> 
struct Class1 {
    template<int j, std::enable_if_t<i % j == 0>* = 0> 
    void foo() {

    }
};

此解决方案的优点是如果foo() 存在其他重载,编译器将尝试它们而不是给出硬错误。正如你在这个Live Example 看到的,编译器给出的错误是:

main.cpp: In function 'int main()':
main.cpp:12:24: error: no matching function for call to 'Class1<3>::foo()'
    Class1<3>{}.foo<2>();
                       ^

这意味着错误发生在用户的代码中,而不是在您的标头中,并且如果在该函数不起作用时有替代函数,编译器将尝试其他重载。

【讨论】:

  • 谢谢!有没有办法使用 enable_if 而不是 enable_if_t?
  • 是的。您可以使用typename std::enable_if&lt;...&gt;::type,也可以定义自己的别名template&lt;bool b, typename T = void&gt; using enable_if_t = typename enable_if&lt;b, T&gt;::type;。我推荐使用别名,因为它使所有内容都更具可读性。
【解决方案3】:

编辑

您真正要求的是使编写代码成为错误,我很遗憾地说这不太可能。我不认为那里有一个 IDE 可以从字面上阻止您编写无效代码。但是,如果您在下面实现我的解决方案之一(编译时解决方案),那么足够先进的 IDE 将能够在您点击编译之前为您提供您编写的代码错误的信息。

像 Visual Studio 这样的编译器本质上会在后台为您“编译”东西,然后用红色波浪线在错误代码下划线(阅读:不会编译)。

下面的编译时检查答案

您为您的 Class2 结构提出了几种可能性,所以让我们分别解决:

首先,我们将从您定义的Class1 开始:

template<int i> 
struct Class1{
// how to define a "foo" function that is only callable 
// if Class2's j is evenly divisble by i?
};

您的第一种可能性是Class2 有一个模板化的j 参数:

template<int j>
struct Class2
{
    //...
};

一个好的解决方案是模板Class1foo 方法,然后包含一个static_assert,这是一个编译时 断言。这是因为ij 在编译时是已知的。

现在Class1 看起来像这样:

template<int i> 
struct Class1{
   template<int j>
   void foo()
   {
       static_assert(j%i==0, "j is not evenly divisible by i");
       std::cout << "j is evenly divisble by i" << std::endl;
   }   
};

Class2 可以像这样调用foo

template<int j>
struct Class2
{
    void CallFoo()
    {
        Class1<2> c1;
        c1.foo<j>(); // works
        
        //Class1<3> c2;
        //c2.foo<2>(); // fails static assert  
    }
};

Demo

您提到的另一种可能性是Class2 可能有一个j 的成员变量。只要该成员变量是constexpr(结果也是static),您就可以完成此操作:

struct Class2
{
    static constexpr int j = 4;
    void CallFoo()
    {
        Class1<2> c1;
        c1.foo<j>(); // works
        
        //Class1<3> c2;
        //c2.foo<2>(); // fails static assert 
    }
};

constexpr 在这里定义了一个 compile-time 常量。因此,可以保证在编译时知道该值,并且我们的 static_assert 将起作用。

如果j不是 constexpr,那么我们无法实现编译时断言。此时,您将被降级到运行时异常处理:

template<int i> 
struct Class1{
   void foo(int j)
   {
       if (j%i != 0)
          throw std::invalid_argument("j is not evenly divisible by i");
       std::cout << "j is evenly divisble by i" << std::endl;
   }   
};

struct Class2
{
    int j = 4;
    void CallFoo()
    {
        Class1<2> c1;
        c1.foo(j); // works
        j = 3;
        c1.foo(j); // throws
    }
};

Demo

【讨论】:

  • 谢谢。供您参考,这是我想要达到的方向:github.com/ShahOdin/Broker 我通过创建代理类来从基类的映射中获取模板类,从而获得了一个技巧。想知道我可以将这种基于枚举的方法扩展多远。
最近更新 更多