3

我有一个类,让我们Foo用几种方法来调用它:

template<typename T>
class Foo {
public:
   Foo()               { /* ... */ }
   bool do_something() { /* ... */ }

   // This method should be callable only if:
   // std::is_floating_point<T>::value == true
   void bar() { 
      // Do stuff that is impossible with integer
   }
};

我希望能够同时构造Foo<double>和但是当类型 T 不是浮点类型时Foo<int>我不想允许调用。bar()我还希望在编译时而不是在运行时生成错误。所以,我想要的是:

Foo<double> a;
a.bar();                        // OK
Foo<int> b;
bool res = b.do_something();    // OK
b.bar();                        // WRONG: compile error

我尝试了很多事情(使用类似thisthis oneenable_if的帖子),但我不能再使用. 例如:intFoo

typename std::enable_if<std::is_floating_point<T>::value>::type
bar() { /* ... */ } 

main.cpp:112:28:   required from here
foo.h:336:5: error: no type named ‘type’ in ‘struct std::enable_if<false, void>’
 bar() {

如何限制bar()对浮点类型的使用,但允许在其他地方使用整数类型?

4

2 回答 2

12
void bar() { 
   static_assert(std::is_floating_point<T>::value,
                 "this method can only be called for floating-point Foos!");
   // do stuff
}
于 2014-05-09T19:56:56.460 回答
3

基本的解决方案是static_assertbar. 当且仅当有人试图调用它时,才会产生错误。但是,它不会阻止人们找到它,并且错误是“迟到的”,因为它无法启用 SFINAE 检测bar.

作为 的替代方案static_assert,您可以使用 SFINAE 有条件地允许bar参与重载决议,并可能阻止它被发现被调用。您必须小心,因为理论上 SFINAE 必须具有可编译的专业化(否则您的程序格式不正确,不需要诊断),并且它仅适用于template方法。

private:
  struct blocked {};
public:
  template<typename Unused=void, 
           typename=typename std::enable_if< 
                                     std::is_same<Unused, blocked>::value ||                            
                                     std::is_floating_point<T>::value 
                                           >::type>
  void bar() {
    // code
  }

这会阻止bar在重载解析期间考虑该方法,而不是使选择它成为错误。这是一个微妙的区别,但如果bar被覆盖,则很重要。

这很钝。在带有精简概念的 C++1y 中,requires子句应该在template没有上述钝器的情况下在 a 中做正确的事情。

在实践中,blocked可能不需要该类型,因为我不知道编译器实际上会强制执行template没有有效特化的 a 的格式错误。该子句可能允许未来的修订在实例化之前进行更多检查。

我能想到的最后一种方法涉及一个 CRTP 基类,该基类专门基于您要创建/销毁的特征bar

template<typename D, bool test>
struct maybe_bar {};
template<typename D>
struct maybe_bar<D, true> {
  D* self() {
    static_assert( std::is_base_of< maybe_bar, D >::value, "CRTP failure" );
    return static_cast<D*>(this);
  }
  D const* self() const{
    static_assert( std::is_base_of< maybe_bar, D >::value, "CRTP failure" );
    return static_cast<D const*>(this);
  }
  void bar() {
    // use self() in here to get at your this pointer instead of this
  }
};

然后在使用点:

template<typename T>
struct my_type:maybe_bar<T, std::is_floating_point<T>::value> {
};

并且bar是否有条件地创建。

这不那么迟钝,但更冗长,并且它使实现与类主体的其余部分严重脱节。

于 2014-05-09T20:23:22.480 回答