c ++:如果条件不满足,在函数调用时编译时错误?

时间:2016-11-07 15:06:55

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

这是我想要实现的简化示例。因此,它可能看起来有点傻但承担我。假设我有

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 ++中执行此操作?

3 个答案:

答案 0 :(得分:4)

使用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();

更新评论,只需添加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;
}

答案 1 :(得分: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>();
                       ^

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

答案 2 :(得分:1)

修改

你真正要求的是做的的代码是一个错误,我很伤心地说,这不是真的有可能。我不认为有一个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
{
    //...
};

一个很好的解决方案是模板Class1的{​​{1}}方法,然后包含static_assert,这是一个编译时断言。这是有效的,因为fooi在编译时是已知的。

现在j看起来像这样:

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

Demo

您提到的另一种可能性是template<int j> struct Class2 { void CallFoo() { Class1<2> c1; c1.foo<j>(); // works //Class1<3> c2; //c2.foo<2>(); // fails static assert } }; 可能有Class2的成员变量。只要该成员变量为constexpr(结果也是j),您就可以完成此任务:

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将起作用。

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

constexpr

Demo