在参数相关查找之前进行模板替换的任何方法(或解决方法?
Any way for template substitution to happen before argument dependent lookup (or workarounds?)
我想这个问题的基本前提是我试图将enable_if
与参数相关查找(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
功能看看是否可以缩短代码。
无论如何,尝试编译会给出一条消息"错误:变量或字段'序列化'声明为 void"。
我的理解是,这行不通,因为只有在参数相关查找后才评估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 阶段查找)。当您使用类型VectorInt
的对象调用archive
时,对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>::type
Bar
。编译器无法为每个 T 实例化Foo
,这是不可能的。
我们想使用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参数为 false,那么您将不会对应于任何类型,并且实例化格式不正确:经典 SFINAE。
这个答案已经很长了,但enable_if
本身就是一个相当深的话题;上面给出的形式在这里有效,但不适用于不相交的重载集。我建议阅读更多关于ADL,模板参数推导,SFINAE和enable_if的信息,而不仅仅是SO(博客文章,Cppcon youtube视频等)。
- 如果C++类在类方法中具有动态分配,但没有构造函数/析构函数或任何非静态成员,那么它仍然是POD类型吗
- 当可输入框在窗口中处于活动状态时获得通知的任何方法
- 无法"this"指针传递到另一个类并在 CPP 中调用该类的任何方法
- 对于不同的语言,是否有任何稳定的序列化方法?
- 任何方法都可以在C++中比较数据结构中的许多方法
- 是否有任何统一的方法可以在 c++ 中创建自定义比较器?
- 查找字符串是否包含字符串向量的任何一个元素的最佳方法
- 是否有任何常规方法可以通知 STL 移动和复制构造函数?
- 在DelphiInterface上实现Dynamic_cast以获取基础对象类的任何方法
- 使用原始字节向量作为提升序列化的存档的任何方法
- C++/(Qt) 指向具有特定方法的任何对象的指针
- 当我的结构中的任何变量发生更改时触发的事件的任何方法
- 是否有任何方法可以将两个boost :: multipRecision :: cpp_int编号乘以
- 通过重新加载DLL,避免使用DLL预加载漏洞,任何更好的方法
- 是否有任何简单的方法(黑客)揭露前向声明隐藏的标头
- 是否有任何方法可以通过使用声明来提高可见度
- 设置用G 构建的二进制版本的任何方法
- 如何从 pjsip 发送电子邮件.是否有任何可用于发送电子邮件的默认方法
- 在参数相关查找之前进行模板替换的任何方法(或解决方法?
- 判断 int 是否在 C++ 中赋值的任何优雅方法