【问题标题】:Can C++ template do this for conditions to improve code?C++ 模板可以为改进代码的条件执行此操作吗?
【发布时间】:2009-01-09 03:06:54
【问题描述】:

我想这样做:
func(conditionA ? pa1 : pa2, conditionB ? pb1 : pb2, conditionC ? pc1 : pc2);

在C风格的函数中,没有问题。但是如果 func() 是模板函数,编译器会报错。 这里 pa1 和 pa2, ... 是不同的类,并且有一个 static 方法 - “convert()”。出于性能考虑,convert() 也被声明为 inline

如果模板不能解决这个问题,就会有一个非常糟糕的 if-else 如下所示。

如果(条件A) { A1型; 如果(条件B) { B1 b 型; 如果(条件C) { C1 c; 函数(a,b,c); } 别的 { C2c; 函数(a,b,c); } } 别的 { B2 b 型; 如果(条件C) { C1 c; 函数(a,b,c); } 别的 { C2c; 函数(a,b,c); } } } 别的 { A2型; 如果(条件B) { B1 b 型; 如果(条件C) { C1 c; 函数(a,b,c); } 别的 { C2c; 函数(a,b,c); } } 别的 { B2 b 型; 如果(条件C) { C1 c; 函数(a,b,c); } 别的 { C2c; 函数(a,b,c); } } }

【问题讨论】:

  • 条件是编译时间常数吗?
  • 您在 cmets 中提到了“convert()”,但代码中没有。你能澄清一下吗?

标签: c++ templates


【解决方案1】:

条件运算符的结果(即p ? a : b中的ab)必须是同一类型。也就是说,你不能这样做:

predicate() ? 3.14 : "sdfsd"

确保您的 pa1pa2 是兼容类型(它们是相同类型、继承自一种类型或转换为兼容类型)。如果您确实有一个 convert 成员函数可以将这些类型转换为兼容类型,那么为什么不直接使用:

conditionA ? pa1.convert() : pa2.convert()

终于,时间不长了。你已经写出了定义。保持通用并继续前进。

【讨论】:

    【解决方案2】:

    使 pa1 和 pa2 从一个公共基类继承,并使用对该祖先的引用作为您的(然后是非模板化的)函数的参数类型。

    【讨论】:

      【解决方案3】:

      您的基本问题是表达式的类型必须在编译时就可以知道

      条件在编译时是否固定且已知?如果是这样,就可以使用元函数来选择每个参数:

      template <bool B>
      struct choose {
          typedef X type;
      };
      
      template <>
      struct choose<false> {
          typedef Y type;
      };
      
      ...
      
      func(choose<a_constant_bool_expr>::type());
      

      (简化为 1 参数情况;对于 3 个参数,您可以定义例如 struct choose1struct choose2struct choose3。)

      否则,您最好的选择是按照 John Zwinck 的建议从一个公共基础派生所有类型。唯一的替代方案是巨大的switch 声明或ifs 列表。

      实际上,这也许是Boost.Variant 的用武之地?

      【讨论】:

        【解决方案4】:

        您遇到的问题是编译时的强类型,而您希望在运行时更改类型。编译 Function 的模板时,它需要知道要提供什么类型。您可以使用不同的类型,但不能在 1 行中全部完成。

        仅在 Function 中需要时才执行 convert() 真的那么昂贵吗?

        远离模板...

        您的描述使方法的长度比看起来更糟,您需要已经定义了 a 的 b 和 c。您还可以将条件简化为 switch 语句。

        如果您将 a、b 和 c 作为一个完整的对象保留,并且可以要求它将值传递给函数。

        class a {
          p1, p2;
          condition;
          val value() { return condition? p1.convert, p2.convert };
        }
        

        你可以让它们有一个接口,该接口对于函数中所需的那些方法是通用的。有几种方法可以做到这一点,但最简单的方法是您可以更改 TypeA1 等的类。在此处添加 IConvertable 之类的父类。

        class IConvertable{
        public: 
          ~IConvertable(){}
          virtual val convert() = 0;
        }
        

        然后在每个类中实现convert调用静态版本的convert。

        【讨论】:

          【解决方案5】:

          这有点难看,但您可以使用位将 3 级嵌套变成单个 switch 语句:

          const unsigned int caseA = 1 << 0;
          const unsigned int caseB = 1 << 1;
          const unsigned int caseC = 1 << 2;
          switch ((conditionA ? caseA : 0) | (conditionB ? caseB : 0) | (conditionC ? caseC : 0)) {
              case 0:                            func(pa2, pb2, pc2); break;
              case caseA:                     func(pa1, pb2, pc2); break;
              case caseB:                     func(pa2, pb1, pc2); break;
              case caseA|caseB:           func(pa1, pb1, pc2); break;
              case caseC:                     func(pa2, pb2, pa1); break;
              case caseA|caseC:           func(pa1, pb2, pc1); break;
              case caseB|caseC:           func(pa2, pb1, pc1); break;
              case caseA|caseB|caseC: func(pa1, pb1, pc1); break;
              default: assert(false); // unreachable
          }
          

          这将您的 3 个二元决策系列拆分为一个 8 路决策,因此更易于推理。有些人可能讨厌它,但我觉得它很可读。

          【讨论】:

            【解决方案6】:

            继续(重复)其他人所说的话......

            不会工作:

            template<class TYPE>
            inline void Function( TYPE & object )
            {
              cout << "Function():  " << object.convert() << endl;
            }
            
            class A
            {
            public:
              static const char * convert() { return "classA"; }
            };
            
            class B
            {
            public:
              static const char * convert() { return "classB"; }
            };
            
            int
            main(int argc)
            {
              A a;
              B b;
            
              Function( argc>1 ? a : b );
            }
            

            由于 ab 是不同的类型,因此正在根据参数类型为您创建模板化的 Function

            但这会起作用:

            template<class TYPE>
            inline void Function( TYPE & object )
            {
              cout << "Function():  " << object.convert() << endl;
            }
            
            class C
            {
            public:
              virtual const char * convert() = 0;
            };
            
            class A : public C
            {
            public:
              static const char * staticConvert() { return "classA"; }
              const char * convert() { return A::staticConvert(); }
            };
            
            class B : public C
            {
            public:
              static const char * staticConvert() { return "classB"; }
              const char * convert() { return B::staticConvert(); }
            };
            
            int
            main(int argc)
            {
              A a;
              B b;
            
              Function( argc>1 ? (C&)a : (C&)b );
            }
            

            虽然我真的应该使用 dynamic_cast...

              Function( argc>1 ? dynamic_cast<C&>(a) : dynamic_cast<C&>(b) )
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2018-07-24
              • 1970-01-01
              • 2011-05-27
              • 1970-01-01
              • 2013-11-05
              • 2013-03-28
              相关资源
              最近更新 更多