【问题标题】:Use one namespace or another in function of a template parameter在模板参数的函数中使用一个或另一个命名空间
【发布时间】:2019-08-08 11:44:59
【问题描述】:

我想在某些情况下使用std::pow(来自cmath)的实现,而在其他情况下,我想使用myspace::pow

namespace myspace
{
    template <typename T>
    T pow(T val, unsigned exp)
    {
        if (exp == 0) return 1;
        return val*pow(val,exp-1);
    }
}

不同的情况由模板参数决定。

template <typename T>
void myFunction()
{
    auto val = pow(2.1,3);
    /* ... */
}

如果T == double,我希望valstd::pow 一起计算。如果T == mydouble,我希望valmyspace::pow 一起计算。现在,我有很多行如auto val = pow(2.1,3);,我想避免为每一行代码检查T 的类型。

struct mydouble { /* ... */ };
myFunction<double>(); // uses std::pow
myFunction<mydouble>(); // uses myspace::pow

我一直在为此烦恼,但找不到解决方案。有什么建议吗?

【问题讨论】:

  • 您可以使用 constexpr if 和 std::is_same::value 检查 T 的类型。
  • @nada 好的,我必须编辑问题才能更清楚。我想避免检查每个命令的类型。
  • 正常函数重载有什么问题?只需将您的pow 写为mydouble pow(mydouble val, unsigned exp) ...mydouble 所在的同一个命名空间中执行此操作),一切都会正常工作。
  • 你知道ADL(argumentdependent lookup)吗?

标签: c++ templates namespaces


【解决方案1】:

我可以提出几种解决方案。


类调度程序 (C++11)

只需实现一个仿函数,它会根据其模板类型选择正确的实现:

template <typename T>
struct PowerAdapter {
  auto operator()(const T& arg, const T& exp) const {
    return std::pow(arg, exp);
  }
};

template <>
struct PowerAdapter<myspace::MyDouble> {
  auto operator()(const myspace::MyDouble& arg, unsigned exp) const {
    return myspace::pow(arg, exp);
  } 
};

你可以按如下方式使用它:

template <typename T>
void myFunction(const T& t) {
  using Pow = PowerAdapter<T>;

  auto val = Pow{}(t, t);
  // ...
}

Complete Code Example


参数依赖查找 (C++98)

如果您的类 MyDouble 与您的 pow 位于同一命名空间中,那么您可以使用此 C++ 规则:

[...] 除了通常的非限定名称查找所考虑的范围和名称空间之外,还在其参数的名称空间中查找函数名称

所以代码:

template <typename T>
void myFunction(const T& t) {
  pow(t, 12);
}

会根据T的命名空间选择合适的pow。 请注意,如果是 double,您希望在 全局命名空间 中从 math.h 中选择 pow(非限定名称查找)。

Complete Code Example

我个人不喜欢这种方式,因为它隐藏了选择机制,而且更难扩展。


if constexpr (C++17)

您可以在编译时选择合适的分支。 将您的选择逻辑包装成适当的函数(或函子)。比如:

template <typename T, typename U>
auto PowerAdapter(const T& val, const U& exp) {
  if constexpr (std::is_same_v<T, myspace::MyDouble>) {
    return myspace::pow(val, exp);
  } else {
    return std::pow(val, exp);
  }
}

Complete Code Example

【讨论】:

    【解决方案2】:

    使用Argument-dependent lookup,您可以轻松实现这一目标

    namespace ns
    {
        template <typename T>
        auto pow(T val, unsigned exp)
        {
            using std::pow;
            std::cout << __FUNCTION__ << '\n';
            return pow(val, exp);
        }
    }
    
    struct mydouble
    {
        double d;
    };
    
    mydouble pow(mydouble val, unsigned exp)
    {
        std::cout << __FUNCTION__ << '\n';
        return val;
    }
    
    int main() 
    {
        ns::pow(mydouble{ 3.14 }, 2); // runs pow
        ns::pow(4, 2);                // runs std::pow
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-11-26
      • 1970-01-01
      • 2017-10-24
      • 1970-01-01
      • 1970-01-01
      • 2017-11-04
      • 2021-01-08
      相关资源
      最近更新 更多