c++ - 在依赖于参数的查找(或解决方法?)之前发生模板替换的任何方式

标签 c++ templates argument-dependent-lookup cereal

我想这个问题的基本前提是我正在尝试将 enable_if 与参数相关查找 (ADL) 一起使用,但我不确定这是否可行。我确实看到了 this page那个

Template argument deduction takes place after the function template name lookup (which may involve argument-dependent lookup) and before template argument substitution (which may involve SFINAE) and overload resolution.

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

这是我正在努力实现的示例:

#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);
}

该示例大体上基于我尝试对 Cereal 库进行的操作。在我的例子中,我有几种不同类型的 vector 和矩阵,虽然我可以使用重载来使函数正确解析,但我很好奇使用 enable_if 功能来查看是否可以缩短代码。

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

我的理解是这行不通,因为 enable_if 仅在参数相关查找后才被评估?是吗?

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

最佳答案

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

也就是说,一般来说,在 C++ 中,允许函数模板推导它们的参数通常比显式指定它们更好,这正是您在这里尝试做的,所以一切都很好。

当你这样做时:

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

调用serialize符合 ADL 条件,并且由于它取决于 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 ,请参阅非推导上下文,这是第一个示例。为了更好地理解原因,请考虑您要求编译器执行的操作:您正在传递一个 VectorInt。并要求编译器找到 T这样 std::enable_if<std::is_same<T, VectorInt>::value, T>::type>恰好是 VectorInt .这似乎是合理的,因为直觉上 enable_if如果第一个参数为真,则只是身份运算符(对于类型)。但是编译器对enable_if没有特别的了解.这相当于说:找到 T这样 Foo<T>::typeBar .编译器无法避免实例化 Foo。对于每一个 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通过了 VectorInt ,它现在将推导出T从传递的参数中,然后它将推导出具有默认值的 U。但是,如果 enable_if 参数为假,则 U 将不对应于任何类型,并且实例化格式错误:经典 SFINAE。

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

关于c++ - 在依赖于参数的查找(或解决方法?)之前发生模板替换的任何方式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49332078/

相关文章:

c++ - Visual Studio 2013 中可能存在的 ADL 错误

c++ - WM_PAINT 不重绘图形

c++ - OMP 目标中对全局数组(malloc 与静态)的不同处理

c++ - 通过指针的指针访问的模板继承的编译错误

c++ - ADL 未选取带有模板参数列表的后缀表达式

c++ - std 类型别名的自定义点

c++ - cpp文件中的C++变量能否定义为特殊符号β

c++ - C++ 中的浮点变量

c++ - 别名模板或专门的派生类?

c++ - 从模板中的子表达式中提取类型