How Can I Avoid Explicitly Specializing Templatized Functions With Argument Dependent Lookup

試著忘記壹切 提交于 2019-12-11 01:47:23

问题


So I've written an answer which uses a templatized function to select object type.

I've defined the types:

struct pt {
    double t;
    double e;
    double c_vis;
    double c_invis;
};

struct pt_weighted : pt {
    double sigma;
};

And my templatized function looks like:

template <typename T>
void foo() {
    for(T point; dataFile >> point;) {
        set.curve.push_back(point); // store point

        data_numPoints++; // collect some stats
        set.curveAvg += point.e;            
    }
}

Given that minimizator_weighted decides which type to use at runtime, I call foo with:

minimizator_weighted ? foo<data_set::pt_weighted>() : foo<data_set::pt>();

Richard Hodges is suggesting using Argument Dependent Lookup (ADL) to avoid "explicitly specialized template functions". I'm just not sure what he means so I thought I'd open a new question so he or someone could explain further in an answer.


回答1:


something along these lines.

Note that I can now add a new point type (or set type) without changing the logic in more than one function. All I have to do is provide ADL overloads of operator>> and do_something for the new types.

So my core logic is now separated from the implementation details of each set type/point type. If I want to use the same code points in some other co-ordinate system I have less code to change (in a real project).

#include <iostream>
#include <vector>

struct pt {
    double t;
    double e;
    double c_vis;
    double c_invis;
};
std::istream& operator>>(std::istream& is, pt& p)
{
  p.c_vis = 0;
  p.c_invis = 0;
  return is >> p.t >> p.e;
}

struct pt_weighted : pt {
    double sigma;
};

std::istream& operator>>(std::istream& is, pt_weighted& p)
{
    auto sigma_correction = [](double& sigma) {
      // whatever this is supposed to do;
    };
  is >> static_cast<pt&>(p) >> p.sigma;
  sigma_correction(p.e);
  return is;
}


template<class Point> struct set
{
  using point_type = Point;   // the type name point_type is now part of the set's type interface, so I can use it in dependent code.
  std::vector<point_type> points;
};

using pt_set = set<pt>;
using pt_weighted_set = set<pt_weighted>;


//
// one implementation of read logic, for all set types.
//
template<class SetType>
void read_set(std::istream& is, SetType& target)
{
  while(is) {
    // using the type protocol here
    auto point = typename SetType::point_type(); // or target.makePoint() ?
    is >> point;
    target.points.push_back(std::move(point));    
  }
}

extern void do_something(pt_set&);
extern void do_something(pt_weighted_set&);

void operation(std::istream& is)
{
  extern bool useSigma();

  // even these lines now no longer need to be repeated
  auto perform = [](auto&& myset) {
    read_set(is, myset);
    do_something(myset);
  };

  if (useSigma())
  {
    perform(pt_weighted_set());
  }
//else if (someOtherCondition()) {
//  perform(someOtherSetType());
//}
  else {
    perform(pt_set());
  }
};



回答2:


In the example below you don't need to specify the type of points that you'll be reading in. Instead, the compiler can figure that out by the argument you've passed into the function. (NOTE: This code block assumes that set, dataFile, and data_numPoints are accessible and mutable from within the function.)

template<class T>
void foo(T point) {
    while (dataFile >> point) {
        set.curve.push_back(point);
        data_numPoints++;
        set.curveAvg += point.e;
    }
}

Now, to call it, you just pass in an instance of the type you care about.

void bar() {
    foo(data_set::pt()); // builds unweighted data set
    foo(data_set::pt_weighted()); // builds weighted data set
}



回答3:


If you want template argument deduction, you have to deduce it from something. For example, you can deduce a function template parameters from it's arguments. For example, you can change your function to this:

template <typename T>
void foo(T p) {
    for(T point = p; dataFile >> point;) {
        set.curve.push_back(point); // store point

        data_numPoints++; // collect some stats
        set.curveAvg += point.e;            
    }
}

Then when you call your function, you can deduce it like this:

data_set::pt_weighted ptw;
data_set::pt pt;

minimizator_weighted ? foo(ptw) : foo(pt);


来源:https://stackoverflow.com/questions/41128744/how-can-i-avoid-explicitly-specializing-templatized-functions-with-argument-depe

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!