使用不依赖于方法模板参数的enable_if
Use a enable_if that does not depend on method's template parameter
>我正在尝试使用std::enable_if
和 SFINAE 来切换纯粹基于类的模板参数的类模板方法的实现。 示例:
#include <type_traits>
template<class T1, class T2>
class Foo {
template<class InnerT, class ... Args>
typename std::enable_if<std::is_same<T1, T2>::value, void>::type
bar(InnerT param) {};
template<class InnerT, class ... Args>
typename std::enable_if<!std::is_same<T1, T2>::value, void>::type
bar(InnerT param) {};
};
int main() {
Foo<int, int> f;
}
在这里,bar()
的行为应该根据T1
和T2
是否相同而有所不同。但是,此代码无法编译。GCC和clang都没有告诉我任何有用的东西。我怀疑问题在于std::enable_if
条件不依赖于bar()
的参数,即不依赖于标准第17.8.2段第8点中规定的直接上下文。
此假设得到了以下事实的支持:此代码编译良好:
#include <type_traits>
class DummyClass {};
template<class T1, class T2>
class Foo {
template<class InnerT, class ... Args>
typename std::enable_if<std::is_same<T1, T2>::value ||
std::is_same<InnerT, DummyClass>::value, void>::type
bar(InnerT param) {};
template<class InnerT, class ... Args>
typename std::enable_if<!std::is_same<T1, T2>::value ||
std::is_same<InnerT, DummyClass>::value, void>::type
bar(InnerT param) {};
};
int main() {
Foo<int, int> f;
}
现在std::enable_if
内的表达式依赖于"直接上下文",即InnerT
,即使表达式的那部分总是计算为false
。
看起来我可以将其用作解决方法,但这感觉真的很笨拙和丑陋。你如何"正确"解决这个问题?我有一个想法是添加一个额外的模板参数(称之为DummyType
)到bar()
,默认为 例如DummyType = T1
,然后检查std::is_same<DummyType, T2>
,但是bar()
需要一个参数包的事实使这是不可能的(或者它...?
与其尝试将 SFINAE 变成两个实现,不如使用正常的重载分辨率。
#include <type_traits>
#include <iostream>
template<class T1, class T2>
class Foo {
template<class InnerT, class ... Args>
void do_bar(InnerT param, std::true_type, Args... args) { std::cout << "same" << std::endl; }
template<class InnerT, class ... Args>
void do_bar(InnerT param, std::false_type, Args... args) { std::cout << "not same" << std::endl; }
public:
template<class InnerT, class ... Args>
void bar(InnerT&& param, Args&&... args)
{
do_bar(std::forward<InnerT>(param), std::is_same<T1, T2>{}, std::forward<Args>(args)...);
}
};
int main() {
Foo<int, int> f1;
Foo<int, double> f2;
f1.bar(1, 2, 3);
f2.bar("Hello");
}
现场观看
要从注释中展开:
我有一个想法是在
bar()
中添加一个额外的模板参数(称之为DummyType
),默认为 例如DummyType = T1
,然后检查std::is_same<DummyType, T2>
,但是bar()
需要一个参数包的事实使这是不可能的(或者它...?
其实不然。完全按照你猜到的去做是行不通的,会奏效。
#include <type_traits>
template<class T1, class T2>
struct Foo {
template<class InnerT, class ... Args, class DummyType = T1>
typename std::enable_if<std::is_same<DummyType, T2>::value, void>::type
bar(InnerT param) {};
template<class InnerT, class ... Args, class DummyType = T1>
typename std::enable_if<!std::is_same<DummyType, T2>::value, void>::type
bar(InnerT param) {};
};
int main() {
Foo<int, int> f;
f.bar(3); // InnerT = int; Args = empty; DummyType = int.
f.bar<int, void, short>(4); // InnerT = int; Args = void, short; DummyType = int.
}
但是,如果我将 DummyType 添加为第二个模板参数,然后传递一个应该进入包的模板参数列表 - 编译器现在如何第二个参数不应该进入 DummyType,而是成为 Args 的一部分的第一个东西?
这就是我添加为最后一个参数的原因。如果模板非包参数具有默认值,则允许它们遵循模板包参数。编译器将使用所有显式指定的参数Args
,并且无论您指定哪个参数,都将使用DummyType = T1
。
我怀疑问题是enable_if条件不依赖于柱的参数,
完全。
我有一个想法是添加一个额外的模板参数(称之为DummyType)到bar,默认为例如DummyType = T1,然后检查std::is_same
我通常看到的正是这个解决方案。
但是 bar 采用参数包的事实使这是不可能的(或者它...?
否,如果您将DummyType
放在InnerT
之前
template <typename D1 = T1, typename InnerT, typename ... Args>
typename std::enable_if<std::is_same<D1, T2>::value>::type
bar (InnerT param) { std::cout << "- true version" << std::endl; }
template <typename D1 = T1, typename InnerT, typename ... Args>
typename std::enable_if<!std::is_same<D1, D2>::value>::type
bar (InnerT param) { std::cout << "- false version" << std::endl; }
这非常有效。
此解决方案的缺点是您可以在解释D1
类型bar()
"劫持">
Foo<int, int> f;
f.bar(0); // print "- true version"
f.bar<long>(0); // print "- false version"
但是你可以解决这个问题,强加T1
和D1
template <typename T1, typename T2>
struct Foo {
template <typename D1 = T1, typename InnerT, typename ... Args>
typename std::enable_if< std::is_same<D1, T2>::value
&& std::is_same<D1, T1>::value>::type
bar (InnerT param) { std::cout << "- true version" << std::endl; }
template <typename D1 = T1, typename InnerT, typename ... Args>
typename std::enable_if< ! std::is_same<D1, T2>::value
&& std::is_same<D1, T1>::value>::type
bar (InnerT param) { std::cout << "- false version" << std::endl; }
};
现在你不能再"劫持"bar()
了。
- 如何反转整数参数包
- 使用C++库在Android项目中修改gradle中的cmake参数,用于插入指令的测试
- 如何使用默认参数等选择模板专业化
- 模板参数替换失败,并且未完成隐式转换
- 具有默认模板参数的多态类的模板推导失败
- lambda参数转换为constexpr技巧,然后获取带链接的数组
- 将数组作为参数传递给函数安全吗?作为第三方职能部门,可以探索他们想要的之外的其他元素
- 函数调用中参数的顺序重要吗
- 部分定义/别名模板模板参数
- 模板-模板参数推导:三个不同的编译器三种不同的行为
- 使用不带参数的函数访问结构元素
- 基于另一个成员参数将函数调用从类传递给它的一个成员
- 如何在OMNET++中指定与命令行参数组合的输出文件名
- 如何使用Luacneneneba API正确读取字符串和表参数
- 在派生函数中指定void*参数
- 视图中的参数推导失败:take_while
- static_assert在宏中,但也可以扩展到可以用作函数参数的东西
- 使用指向成员的指针将成员函数作为参数传递
- 如何将enable-if与模板参数和参数包一起使用
- 如何"enable"基于模板参数的函数?