我认为这个问题的基本前提是我正在尝试使用enable_if
和Argument Dependent Lookup(ADL),但我不确定它是否可行。我确实在this page看到了
模板参数推导发生在函数模板名称查找(可能涉及依赖于参数的查找)之后和模板参数替换(可能涉及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
功能来查看是否可以缩短代码。
无论如何,尝试编译会给出一条消息“错误:变量或字段'serialize'声明为void”。
我的理解是,这不起作用,因为enable_if
仅在参数依赖查找后进行评估?是吗?
对于那些想要玩这个的人,我在repl.it上有代码:https://repl.it/repls/HalfBlandJumpthreading
答案 0 :(得分:1)
在你的例子中有两个不同的事情:有(函数)模板参数推导,还有参数依赖查找(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>
恰好是{{1} }}。这似乎是合理的,因为如果第一个参数为true,直觉VectorInt
只是身份运算符(对于类型)。但是编译器对enable_if
没有特别的了解。这相当于说:查找enable_if
,T
为Foo<T>::type
。编译器无法为每个T实例化Bar
,这是不可能的。
我们想要使用enable_if,但不能禁用扣除。使用Foo
的最佳方法通常是默认模板参数:
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
,然后它将推断出U有默认值。但是,如果enable_if参数为false,则U将不对应任何类型,并且实例化格式不正确:经典SFINAE。
这个答案已经很长了,但T
本身是一个相当深刻的话题;上面给出的表单在这里工作,但不适用于不相交的重载集。我建议阅读更多有关ADL,模板参数演绎,SFINAE和enable_if的内容,不仅仅是SO(博客文章,Cppcon youtube视频等)。