【问题标题】:auto with ternary operator and templates带三元运算符和模板的自动
【发布时间】:2016-11-17 07:09:28
【问题描述】:

我有一个模板类

template <typename T>
class foo;

T有2个有效值,分别对应:

using fooT1 = class foo<T1>;
using fooT2 = class foo<T2>;

我想编写如下代码:

const auto* fooPtr = useFooT1 ? getFooT1Ptr() : getFooT2Ptr();

因为在此函数中使用fooPtr 的代码不取决于fooPtr 是fooT1 还是fooT2 类型

但是,我收到以下编译器错误:

error: conditional expression between distinct pointer types ...

我了解,根据 C++ 标准,应该有一个可以强制转换为的通用类型,因此这种方法可能行不通。

在不复制大量代码的情况下实现此功能的好方法是什么?

【问题讨论】:

  • 使用 fooPtr 将该代码拆分为一个单独的函数模板,该模板根据指针的类型进行模板化?
  • @T.C.是的,这是我心中的一种选择。想知道我是否还有其他选择。
  • @vigs1990 我的博客上有提醒之类的东西:dev-jungle.blogspot.de/2014/02/…

标签: c++ templates c++11 auto


【解决方案1】:

确实,C++ 是静态类型的,所以变量的类型不能依赖于运行时条件。

相反,将通用代码放入模板中:

template <typename T> doStuff(foo<T> * f) {
    // stuff that works with any `foo` type
}

并根据运行时变量调用不同的特化

if (useFooT1) {
    doStuff(getFooT1Ptr());
} else {
    doStuff(getFooT2Ptr());
}

【讨论】:

    【解决方案2】:

    如果布尔变量 useFooT1 在编译时已知,您可以实现编译时开关,例如

    FooT1 const* getFooPtr(std::true_type /* useFooT1 */) { 
        return getFooT1Ptr(); 
    }
    FooT2 const* getFooPtr(std::false_type /* !useFooT1 */) { 
        return getFooT2Ptr(); 
    }
    /* ... */
    auto const* fooPtr = getFooPtr(std::integral_constant<bool, useFooT1>());
    

    这也可以概括为两种以上的类型,开关可以直接依赖于类型T1/T2。

    另请参阅:Is it possible to use tag dispatching to determine return typeSwitch passed type from template 以及许多其他人。

    【讨论】:

      【解决方案3】:

      下面是一个示例(实际上是 2 种不同的方法:V1/V2)如何使用一个可以在内部使用不同类型的 std:: 容器的函数。请注意,使用的容器类型实际上可以改变函数的结果(因为容器的内部排序),这就是我在这里展示的内容。函数 returnMaxPointV1/2 采用 bool 参数,内部有部分通用代码和部分专用代码。这是与使用模板相反的方法,其中函数调用者必须是专门的(!在编译时!),但函数体是通用的。这里调用者是通用的(!在运行时!),但主体是专门的。

      #include <vector>
      #include <set>
      #include <iostream>
      #include <thread>         // std::this_thread::sleep_for
      #include <chrono>         // std::chrono::seconds
      
      struct Point2D {
          int x1;
          int x2;
      
          bool operator < (const Point2D& rhs) const {
              return x1-10*x2 < rhs.x1-10*rhs.x2;
          }
      };
      
      
      Point2D points[4] = { { 10,2 },{ 2,2 },{ 0,1 },{ 10,4 } };
      
      
      namespace returnMaxInlines
      {
          inline  Point2D FindMax(Point2D a, Point2D b) {
              return (a.x1 > b.x1) ? a : b;
          }
      }
      
      /*
      The function returnMaxPoint fills a container with points that satisfy the condition x2 >= 2.
      You can select what kind of containter to use  by using the bool containterTypeSet
      the function then finds the point with maximum x1 in the container, but because
      there are some points with the same x1 it will return the first one encountered,
      which may be a different one depending on the container type,
      because the points may be differently ordered in the container.
      */
      Point2D returnMaxPointV1(bool containterTypeSet)
      {
          using namespace returnMaxInlines;       // the inline function is only accessible locally in this function (because of the namespace)
      
          Point2D     maxPoint = { 0,0 };
          if (containterTypeSet == true) {
              std::set<Point2D>   container;
              for (int cnt = 0; cnt < 4; ++cnt) {
                  if (points[cnt].x2 >= 2) {
                      container.insert(points[cnt]);
                  }
              }
              for (auto it = container.begin(); it != container.end(); ++it) {
                  maxPoint = FindMax(maxPoint, *it);      // this part of the code is generic for both cases of bool containterTypeSet, it is an inline function
              }
          }
          else {
              std::vector<Point2D>    container;
              for (int cnt = 0; cnt < 4; ++cnt) {
                  if (points[cnt].x2 >= 2) {
                      container.push_back(points[cnt]);
                  }
              }
              for (auto it = container.begin(); it != container.end(); ++it) {
                  maxPoint = FindMax(maxPoint, *it);      // this part of the code is generic for both cases of bool containterTypeSet, it is an inline function
              }
          }
          return maxPoint;
      }
      
      /* Alternative implementation
          No inline function is needed, but you need to have all variables for both options declared in scope
          and you need to add many if-statements, thus branching (but maybe the compiler can optimize this out (which will yield some kind of version V1)
      */
      
      Point2D returnMaxPointV2(bool containterTypeSet)
      {
          Point2D                 maxPoint = { 0,0 };
          std::set<Point2D>       containerSet;
          std::vector<Point2D>    containerVector;
      
          // specialized code
          if (containterTypeSet) {
              for (int cnt = 0; cnt < 4; ++cnt) {
                  if (points[cnt].x2 >= 2) {
                      containerSet.insert(points[cnt]);
                  }
              }
          }
          else{
              for (int cnt = 0; cnt < 4; ++cnt) {
                  if (points[cnt].x2 >= 2) {
                      containerVector.push_back(points[cnt]);
                  }
              }
          }
          std::set<Point2D>::iterator         SetIterator;
          std::vector<Point2D>::iterator      VectorIterator;
      
          const Point2D   *ScopePoint;
          bool            foolCompiler;       // the compiler was tripping if both types of the tenary operator (?:) were not of the same type, so I made the statement such that the statement was of type bool.
      
          for(foolCompiler = containterTypeSet? ((SetIterator = containerSet.begin()) == SetIterator): ((VectorIterator = containerVector.begin()) == VectorIterator);
              containterTypeSet ? SetIterator != containerSet.end() : VectorIterator != containerVector.end();
              foolCompiler = containterTypeSet ? ((SetIterator++) == SetIterator) : ((VectorIterator++) == VectorIterator)
              ){
              ScopePoint = containterTypeSet ? &(*SetIterator) : &(*VectorIterator);
              // generic code 
              maxPoint = (maxPoint.x1 > ScopePoint->x1) ? maxPoint : *ScopePoint;
          }
      
          return maxPoint;
      }
      
      int main()
      {
          Point2D result;
      
          result = returnMaxPointV1(true);
          std::cout << "result1: (" << result.x1 << "," << result.x2 << ")" << std::endl;
          result = returnMaxPointV1(false);
          std::cout << "result2: (" << result.x1 << "," << result.x2 << ")" << std::endl;
      
          result = returnMaxPointV2(true);
          std::cout << "result3: (" << result.x1 << "," << result.x2 << ")" << std::endl;
          result = returnMaxPointV2(false);
          std::cout << "result4: (" << result.x1 << "," << result.x2 << ")" << std::endl;
      
          std::this_thread::sleep_for(std::chrono::seconds(10));
      }
      

      程序的输出是:

      result1: (10,2)
      result2: (10,4)
      result3: (10,2)
      result4: (10,4)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-03-07
        • 2017-05-05
        • 2012-08-14
        • 2021-09-19
        • 2016-07-05
        • 2019-01-21
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多