1

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

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

和 aclass2具有固定int j变量:由此类 int 模板化或具有成员变量。我希望class2实例只有在满足特定条件时才能调用foo(),在这种情况下,我想确保 say (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任务中指定的角色 enums() 匹配时。本质上,这应该是基于枚举的设计,没有动态多态性。在 C++ 中有没有更好的方法来做到这一点?

4

3 回答 3

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;
}
于 2016-11-07T15:15:03.553 回答
3

与其他解决方案相反,我建议使用 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>();
                       ^

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

于 2016-11-07T15:48:03.407 回答
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
{
    //...
};

一个好的解决方案是 templateClass1foo方法,然后包含 a static_assert,这是一个编译时断言。这是有效的,因为i并且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这样调用:

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

演示

您提到的另一种可能性是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在这里定义了一个编译时常量。因此,可以保证在编译时知道该值,并且我们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
    }
};

演示

于 2016-11-07T15:35:15.340 回答