2

我想这个问题的基本前提是我试图enable_if与 Argument Dependent Lookup (ADL) 一起使用,但我不确定它是否可能。我确实在此页面上看到

模板参数推导发生在函数模板名称查找(可能涉及依赖于参数的查找)之后和模板参数替换(可能涉及 SFINAE)和重载解析之前。

所以我想这行不通,但本着学习的精神,我想把这个问题提出来。

这是我想要发生的事情的一个例子:

#include <iostream>

namespace lib1 {
  template <typename T>
  void archive(T & t)
  {
    serialize(t);
  }
}

namespace lib2 {
struct VectorInt {
  int x;
  int y;
};

struct VectorDouble {
  double x;
  double y;
};

template<typename T>
void serialize(std::enable_if<std::is_same<T, VectorInt>::value, T>::type & vect) {
  std::cout << vect.x << std::endl;
}

// maybe do something different with VectorDouble. Overloading would work,
// but I'm curious if it can be made to work with enable_if

}

int main() {
  lib2::VectorInt myvect;
  myvect.x = 2;
  lib1::archive(myvect);
}

该示例大致基于我正在尝试对谷物库进行的操作。就我而言,我有几种不同类型的向量和矩阵,虽然我可以使用重载来正确解析函数,但我很想使用该enable_if功能来查看是否可以缩短代码。

无论如何,尝试编译会给出一条消息“错误:变量或字段'序列化'声明为无效”。

我的理解是这不起作用,因为enable_if仅在依赖于参数的查找之后才评估?是对的吗?

对于那些想玩这个的人,我在 repl.it 上有代码:https ://repl.it/repls/HalfBlandJumpthreading

4

1 回答 1

1

您的示例中发生了两件事:(函数)模板参数推导,以及参数相关查找(ADL)。如果您开始尝试显式指定模板参数(嘿,它是 C++),这两者之间的关系会稍微复杂一些,您可以在此处阅读更多内容:http: //en.cppreference.com/w/cpp/language/adl(在注释部分)。

也就是说,通常在 C++ 中,允许函数模板推断它们的参数而不是显式指定它们通常更好,这就是你在这里尝试做的事情,所以一切都很好。

当你这样做时:

namespace lib1 {
  template <typename T>
  void archive(T & t)
  {
    serialize(t);
  }
}

对 ADL的调用serialize符合条件,并且由于它依赖于 t,因此它被推迟到模板被实例化,因为t需要的类型(这称为 2 阶段查找)。当您archive使用 类型的对象调用时VectorInt,对 的调用serialize将在VectorInt. 一切正常。问题出在这段代码中:

template<typename T>
void serialize(std::enable_if<std::is_same<T, VectorInt>::value, T>::type & vect) {
  std::cout << vect.x << std::endl;
}

您没有明确指定模板参数,因此必须推导出它们。但是您在此处提供的表格不允许演绎:http ://en.cppreference.com/w/cpp/language/template_argument_deduction ,请参阅第一个示例非演绎上下文。为了更好地理解原因,请考虑您要求编译器做什么:您正在传递 a并要求编译器VectorInt找到恰好是. 这似乎是合理的,因为如果第一个参数为真,直觉上只是恒等运算符(对于类型)。但是编译器对. 这相当于说:找到这样的。编译器无法通过实例化来做到这一点Tstd::enable_if<std::is_same<T, VectorInt>::value, T>::type>VectorIntenable_ifenable_ifTFoo<T>::typeBarFoo对于每一个 T,这是不可能的。

我们想使用 enable_if,但不是以禁用扣除的方式。最好的使用enable_if方式通常是在默认模板参数中:

template<typename T, typename U = typename std::enable_if<std::is_same<T, VectorInt>::value>::type >
void serialize(T& vect) {
  std::cout << vect.x << std::endl;
}

U不用于任何事情,但是当serialize传递 a时VectorInt,它现在T将从传递的参数中推断出,然后推断出具有默认值的 U。但是,如果 enable_if 参数为 false,则 U 将不对应任何类型,并且实例化格式不正确:经典 SFINAE。

这个答案已经很长了,但enable_if它本身就是一个相当深的话题;上面给出的形式在这里有效,但不适用于不相交的重载集。我建议阅读更多关于 ADL、模板参数推导、SFINAE 和 enable_if 的内容,而不是 SO(博客文章、Cppcon youtube 视频等)。

于 2018-03-17T04:44:40.367 回答