简体   繁体   English

通过使用traits使用模板类在编译时抛出错误来禁用函数

[英]Disable a function by throwing error at compile-time with template class using traits

I have a class, let's call it Foo with several methods: 我有一个类,让我们用几种方法称它为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
   }
};

I would like to be able to construct both Foo<double> and Foo<int> But I don't want to allow calls to bar() when the type T is not a floating point type. 我希望能够构造Foo<double>Foo<int>但是当T类型不是浮点类型时,我不想允许调用bar() I also want the error to be generated at compile-time and not at the run-time. 我还希望在编译时生成错误,而不是在运行时生成错误。 So, what I want is: 所以,我想要的是:

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

I tried a lot of things with enable_if (with posts like this or this one ) but I can't use anymore the int type with Foo . 我尝试了很多事情enable_if (与像帖子这个这一个 ),但我不能再使用的intFoo For example: 例如:

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

How can I constrain the use of bar() to floating point types but allow integer type to use in other places? 如何将bar()的使用限制为浮点类型,但允许整数类型在其他地方使用?

void bar() { 
   static_assert(std::is_floating_point<T>::value,
                 "this method can only be called for floating-point Foos!");
   // do stuff
}

The basic solution is to use a static_assert within the body of bar . 基本解决方案是在bar体内使用static_assert This generates an error if and only if someone tries to call it. 当且仅当有人试图调用它时,这会产生错误。 It does not, however, block people from finding it, and the error is "late" in that it doesn't enable SFINAE detection of the existence of bar . 但是,它并没有阻止人们发现它,并且错误是“迟到”,因为它不能使SFINAE检测到bar的存在。

As an alternative to static_assert , you can use SFINAE to conditionally allow bar to participate in overload resolution, and possibly block it from being found to be called at all. 作为static_assert的替代方法,您可以使用SFINAE有条件地允许bar参与重载决策,并可能阻止它被发现被调用。 You have to be careful, because the SFINAE must theoretically have a specialization that compiles (otherwise your program is ill-formed with no diagnosis required), and it only works on a template method. 你必须要小心,因为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
  }

This blocks the method bar from being considered during overload resolution, rather than making selecting it an error. 这会阻止方法bar在重载解析期间被考虑,而不是选择它作为错误。 This is a subtle difference, but matters a lot if bar is overridden. 这是一个微妙的区别,但如果bar被覆盖则很重要。

which is pretty obtuse. 这是非常迟钝的。 In C++1y with concepts lite, requires clauses are supposed to do the right thing within a template without the above obtuse stuff. 在带有概念lite的C ++ 1y中, requires子句应该在template做正确的事情而没有上述钝的东西。

In practice, the blocked type is probably not needed, as I am not aware of a compiler that actually enforces the ill-formedness of a template that has no valid specializations. 在实践中,可能不需要blocked类型,因为我不知道编译器实际上强制执行没有有效特化的template的不良形式。 That clause is probably there to allow future revisions to do more checking prior to instantiation. 该子句可能允许将来的修订版在实例化之前进行更多的检查。

The final method I can think of involves a CRTP base class that specializes based on the trait you want to create/destroy bar based on. 我能想到的最后一个方法涉及一个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
  }
};

then at the point of use: 然后在使用点:

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

and bar is conditionally created or not. bar是有条件地建立或没有。

This is less obtuse, but more verbose, and it places the implementation seriously out-of-line with the rest of the class body. 这不是那么迟钝,而是更冗长,并且它使得实现与类体的其他部分严重脱节。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM