SFINAE 没有避免模棱两可的调用
Ambiguous call not avoided by SFINAE
编译这段代码:
#include <iostream>
template <int N>
struct TestClass {
template <int N2, typename std::enable_if<N2 == N, int>::type = 0>
void doAction() { std::cout << "TestClass::doAction<" << N << ">();\n"; }
};
struct HostClass : public TestClass<1>, public TestClass<2> {
};
int main(int argc, const char * argv[]) {
HostClass hostClass;
hostClass.doAction<1>();
hostClass.doAction<2>();
return 0;
}
导致不明确的调用错误,因为 doAction
同时在 TestClass<1>
和 TestClass<2>
父 类.
中
main.cpp:33:15: Member 'doAction' found in multiple base classes of different types
但是 std::enable_if
不会消除这种歧义吗?
编辑:
我认为造成这种歧义的真正原因与这个问题相同:
Why do multiple-inherited functions with same name but different signatures not get treated as overloaded functions?
歧义可以通过 using
关键字解决,如答案所示:
#include <iostream>
template <int N>
struct TestClass {
template <int N2, typename std::enable_if<N2 == N, int>::type = 0>
void doAction() { std::cout << "TestClass::doAction<" << N << ">();\n"; }
};
struct HostClass : public TestClass<1>, public TestClass<2> {
using TestClass<1>::doAction;
using TestClass<2>::doAction;
};
int main(int argc, const char * argv[]) {
HostClass hostClass;
hostClass.doAction<1>(); // OK, compile
hostClass.doAction<2>(); // OK, compile
//hostClass.doAction<3>(); // OK, doesn't compile : "candidate template ignored: disabled by 'enable_if' [with N2 = 3]"
return 0;
}
我不知道这是否是@skypjack 回答的意思,但我还是让它作为替代方法。
它将(让我说)删除替换后的两个函数之一。
无论如何,首先编译器必须决定当你调用它时你打算使用哪个函数 doAction<1>
, 然后 它可以继续进行替换并最终扔掉因为sfinae而选择的功能。
在调用点,它们都是有效的候选者,调用实际上是不明确的。
请注意,正如@Peregring-lk 在评论中所建议的那样,TestClass<1>::doAction
和 TestClass<2>::doAction
是放置在不同命名空间中的两个不同函数,它们不是同一函数的重载。
这实际上是一个常见的误解来源。
您可以通过以下方式轻松解决问题:
#include <iostream>
template <int N>
struct TestClass {
void doAction() { std::cout << "TestClass::doAction<" << N << ">();\n"; }
};
struct HostClass : public TestClass<1>, public TestClass<2> {
template<int N>
void doAction() { return TestClass<N>::doAction(); }
};
int main(int argc, const char * argv[]) {
HostClass hostClass;
hostClass.doAction<1>();
hostClass.doAction<2>();
return 0;
}
编译这段代码:
#include <iostream>
template <int N>
struct TestClass {
template <int N2, typename std::enable_if<N2 == N, int>::type = 0>
void doAction() { std::cout << "TestClass::doAction<" << N << ">();\n"; }
};
struct HostClass : public TestClass<1>, public TestClass<2> {
};
int main(int argc, const char * argv[]) {
HostClass hostClass;
hostClass.doAction<1>();
hostClass.doAction<2>();
return 0;
}
导致不明确的调用错误,因为 doAction
同时在 TestClass<1>
和 TestClass<2>
父 类.
main.cpp:33:15: Member 'doAction' found in multiple base classes of different types
但是 std::enable_if
不会消除这种歧义吗?
编辑:
我认为造成这种歧义的真正原因与这个问题相同:
Why do multiple-inherited functions with same name but different signatures not get treated as overloaded functions?
歧义可以通过 using
关键字解决,如答案所示:
#include <iostream>
template <int N>
struct TestClass {
template <int N2, typename std::enable_if<N2 == N, int>::type = 0>
void doAction() { std::cout << "TestClass::doAction<" << N << ">();\n"; }
};
struct HostClass : public TestClass<1>, public TestClass<2> {
using TestClass<1>::doAction;
using TestClass<2>::doAction;
};
int main(int argc, const char * argv[]) {
HostClass hostClass;
hostClass.doAction<1>(); // OK, compile
hostClass.doAction<2>(); // OK, compile
//hostClass.doAction<3>(); // OK, doesn't compile : "candidate template ignored: disabled by 'enable_if' [with N2 = 3]"
return 0;
}
我不知道这是否是@skypjack 回答的意思,但我还是让它作为替代方法。
它将(让我说)删除替换后的两个函数之一。
无论如何,首先编译器必须决定当你调用它时你打算使用哪个函数 doAction<1>
, 然后 它可以继续进行替换并最终扔掉因为sfinae而选择的功能。
在调用点,它们都是有效的候选者,调用实际上是不明确的。
请注意,正如@Peregring-lk 在评论中所建议的那样,TestClass<1>::doAction
和 TestClass<2>::doAction
是放置在不同命名空间中的两个不同函数,它们不是同一函数的重载。
这实际上是一个常见的误解来源。
您可以通过以下方式轻松解决问题:
#include <iostream>
template <int N>
struct TestClass {
void doAction() { std::cout << "TestClass::doAction<" << N << ">();\n"; }
};
struct HostClass : public TestClass<1>, public TestClass<2> {
template<int N>
void doAction() { return TestClass<N>::doAction(); }
};
int main(int argc, const char * argv[]) {
HostClass hostClass;
hostClass.doAction<1>();
hostClass.doAction<2>();
return 0;
}