我尝试使用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
中的表达式取决于&#34;立即上下文&#34;,即InnerT
,即使表达式的该部分始终求值为false
。< / p>
看起来我可以使用它作为一种解决方法,但这感觉真的很丑陋和丑陋。你如何解决这个问题&#34;正确&#34;?我的想法是向DummyType
添加一个额外的模板参数(称之为bar()
),默认为例如DummyType = T1
,然后检查std::is_same<DummyType, T2>
,但是bar()
采用参数包的事实使得这不可能(或者是......?)
答案 0 :(得分:15)
不要尝试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");
}
答案 1 :(得分:7)
从评论中扩展:
我的想法是向
DummyType
添加一个额外的模板参数(称之为bar()
),默认为例如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
。
答案 2 :(得分:2)
我怀疑问题是enable_if条件不依赖于bar的参数,
完全。
我的想法是添加一个额外的模板参数(称之为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; }
这完美无缺。
此解决方案的缺点是您可以“劫持”bar()
来说明D1
类型
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()
了。