我试图用sfinae检查进行 friend 声明遇到了一些麻烦(如果您不想解释“为什么”和“如何”,可以直接进入代码示例)。
基本上,我有一些模板类声明两个私有(private)成员函数。根据模板类型的实例,我想使用一个或另一个函数。
因此,如果我不想编译失败,则无法实例化无法使用的私有(private)函数。因此,我必须通过sfinae检查(独立功能)来调用它。考虑到它是私有(private)的,我必须让我的sfinae检查我类的 friend 。
但是,我无法做到这一点,如下面的(最小)代码所示。我不想更改的东西:A类的原型(prototype)(f1和f2必须保持私有(private)),B1和B2类的原型(prototype)。
我知道评论中的内容为什么会失败(或者我认为是),但是我不知道如何解决。
#include <iostream>
template<class T> class A;
template<class T>
auto sfinae_check(T& t, A<T>& a, int) -> decltype(t.b1(), void());
template<class T>
auto sfinae_check(T& t, A<T>& a, long) -> decltype(t.b2(), void());
template<class T>
class A
{
void f1() { t.b1(); }
void f2() { t.b2(); }
T& t;
//friend auto sfinae_check<>(T &t, A<T> &a, int);//obviously mismatches everything
//friend auto sfinae_check<>(T &t, A<T> &a, int) -> decltype(t.b1(), void()); //failure : no member named b1
//friend auto sfinae_check<>(T &t, A<T> &a, long) -> decltype(t.b2(), void()); //failure : no member named b2
public:
A(T& t) : t(t) {}
void f() { sfinae_check(t, *this, 0); }
};
template<class T>
auto sfinae_check(T& t, A<T>& a, int) -> decltype(t.b1(), void())
{
a.f1();
}
template<class T>
auto sfinae_check(T& t, A<T>& a, long) -> decltype(t.b2(), void())
{
a.f2();
}
struct B1
{
void b1() { std::cout << "b1" << std::endl; }
};
struct B2
{
void b2() { std::cout << "b2" << std::endl; }
};
int main()
{
B1 b1; B2 b2;
A<B1> a1(b1);
a1.f(); //should print b1
A<B2> a2(b2);
a2.f(); //should print b2
}
最佳答案
如果您放弃了不同的名称f1
和f2
并进行标签分配,则可以大大简化整个方案(也可以简化):
template<int> struct tag{};
template<int i> struct priority : priority<i - 1> {};
template<> struct priority <0>{};
template<class T>
auto sfinae_check(T& t, priority<1>) -> decltype(t.b1(), tag<1>{}) { return {}; }
template<class T>
auto sfinae_check(T& t, priority<0>) -> decltype(t.b2(), tag<0>{}) { return {}; }
template<class T>
class A
{
void f(tag<1>) { t.b1(); }
void f(tag<0>) { t.b2(); }
T& t;
public:
A(T& t) : t(t) {}
void f() { f(sfinae_check(t, priority<1>{})); }
};
没有友谊,几乎没有循环依赖性,您会看到所需的确切输出。锦上添花,如果需要的话,增加对另一个过载的支持应该相当容易。
重载的优先级也在此处进行编码(感谢Jarod42提醒我)。由于这些标记在继承链中,因此可以将第二个参数
priority<1>{}
提供给任一重载,但如果两个重载都可行,则它将倾向于更紧密的匹配。