13

简而言之,问题是:
如何static if用纯 c++ 实现 c++11 中提出的功能?

历史和原始问题:
最近我想出了一个这样的问题。我需要一个Sender具有类似接口的类

class Sender
{
   void sendMessage( ... );
   void sendRequest( ... );
   void sendFile( ... );
   // lots of different send methods, not important actually
}

在某些情况下,我需要创建一个DoubleSender,即这个类的一个实例,它会调用它的方法两次,也就是说,当调用一个sendMessage(...)方法时,必须发送两次相同的消息。

我的解决方案:
第一种方法:
有一个isDouble成员,并在每个方法调用结束时进行检查

sendMessage(...) { ... if( isDouble ) { sendMessage( ... ); }

好吧,我不想要这个,因为实际上我最近需要双重发布,而时间关键部分的这部分代码将 98% 是被动的。

第二种方法:
继承一个类,并实现其方法,例如:DoubleSenderSender

void DoubleSender::sendMessage( ... )
{
   Sender::sendMessage(...);
   Sender::sendMessage(...);
}

好吧,这是可以接受的,但会占用大量令人不快的代码空间(真的很多,因为有很多不同的send..方法。

第三种方法:
想象一下我正在使用 c++11 :)。然后我可以使这个类通用并根据 tempalte 参数生成代码的必要部分,使用static if

enum SenderType { Single, Double };
template<SenderType T>
class Sender
{
   void sendMessage(...)
   {
      // do stuff
      static if ( T == Single )
      {
         sendMessage(...);
      }
   }
};

这比以前的解决方案更短、更易于阅读,不会生成额外的代码,而且......它是 c++11,很遗憾我不能在我的工作中使用它。

所以,这就是我提出问题的地方——如何static if在 c++ 中实现模拟?
另外,我将不胜感激有关如何解决我原来的问题的任何其他建议。
提前致谢。

4

4 回答 4

9

引用@JohannesSchaubLitb

使用我在 gcc 上工作的 static_if 可以
以某种有限的方式做到这一点:)

(另见这里

这个技巧涉及对 C++11 中 Lambdas 规范的特定 GCC 解释。因此,它将(很可能)成为针对标准的缺陷报告。这将导致该技巧在最新版本的 GCC 中不再起作用(它在 4.7 中已经不起作用)。

有关 Johanness 的更多详细信息,请参阅下面的评论线程

http://ideone.com/KytVv

#include <iostream>
 
namespace detail {
template<bool C>
struct call_if { template<typename F> void operator<<(F) { } };
 
template<>
struct call_if<true> {
  template<typename F>
  void operator<<(F f) { f(); }
};
}
 
#define static_if(cond) detail::call_if<cond>() << [&]
 
template<bool C, typename T>
void f(T t) {
  static_if(C) {
    t.foo();
  };
}
 
int main() {
  f<false>(42);
}
于 2012-03-30T10:22:33.793 回答
7

为什么不将 send 实现作为 sender 类的策略并使用 CRTP:

template<class Derived>
class SingleSenderPolicy
{
    public:
    template< class memFunc >
    void callWrapperImpl(memFunc f, ...)
    {
        static_cast<Derived *>(this)->f(...);
    }
};

template< class Derived >
class DoubleSenderPolicy
{
    public:
    template< class memFunc >
    void callWrapperImpl(memFunc f, ...)
    {
        static_cast<Derived *>(this)->f(...);
        static_cast<Derived *>(this)->f(...);
     }
};

template< class SendPolicy>
class Sender : public SendPolicy< Sender >
{
public:
    void sendMessage( ... )
    {
       // call the policy to do the sending, passing in a member function that
       // acutally performs the action
       callWrapperImpl( &Sender::sendMessageImpl, ... );
    }

    void doSomethingElse( ... )
    {
       callWrapperImpl( &Sender::doSomethingElseImpl, ... );
    }


protected:
    void sendMessageImpl(... )
    {
        // Do the sending here
    } 

    void doSomethingElseImpl(... )
    {
        // Do the sending here
    } 
};

您类中的公共sendXXX函数只是转发给调用包装器,传入一个实现真正功能的成员函数。这个成员函数将根据SendPolicy类的调用。CRTP 节省了使用 bind 来包装参数和 this 指针与要调用的成员函数。

使用一个函数并不能真正减少代码量,但如果您有很多调用,它可能会有所帮助。

注意:此代码是提供可能解决方案的骨架,尚未编译。

注意:Sender<DoubleSenderPolicy>Sender<SingleSenderPolicy>是完全不同的类型,不共享动态继承关系。

于 2012-03-30T15:00:44.257 回答
5

Most compilers do constant folding and dead code removal, so if you write a regular if statement like this:

enum SenderType { Single, Double };
template<SenderType T>
class Sender
{
   void sendMessage(...)
   {
      // do stuff
      if ( T == Single )
      {
         sendMessage(...);
      }
   }
};

The if branch will get removed when the code is generated.

The need for static if is when the statements would cause a compiler error. So say you had something like this(its somewhat psuedo code):

static if (it == random_access_iterator)
{
    it += n;
}

Since you can't call += on non-random access iterators, then the code would always fail to compile with a regular if statement, even with dead code removal. Because the compiler still will check the syntax for before removing the code. When using static if the compiler will skip checking the syntax if the condition is not true.

于 2012-04-03T21:28:21.077 回答
0
std::string a("hello world");
// bool a = true;
if(std::is_same<std::string, decltype(a)>::value) {
    std::string &la = *(std::string*)&a;
    std::cout << "std::string " << la.c_str() << std::endl;
} else {
    bool &la = *(bool*)&a;
    std::cout << "other type" << std::endl;
}
于 2017-12-06T23:51:25.637 回答