简体   繁体   English

与SFINAE支票结交

[英]Befriending SFINAE checks

I've runned into some trouble trying to make friend declarations with sfinae checks (you can just jump into the code sample if you don't want explanations on "why" and "how"). 我试图用sfinae检查进行朋友声明时遇到了一些麻烦(如果你不想解释“为什么”和“如何”,你可以跳进代码示例)。

Basically, I have some template class declaring two private member functions. 基本上,我有一些模板类声明两个私有成员函数。 Depending on the instantiation of the template type, I want to use either one or the other function. 根据模板类型的实例化,我想使用一个或另一个函数。

So, if I don't want compilation to fail, the private function that I cannot use cannot be instantiated. 因此,如果我不希望编译失败,则无法实例化我无法使用的私有函数。 So, I must called it through an sfinae check (an independent function). 所以,我必须通过sfinae检查(一个独立的功能)来调用它。 Considering it is private, I have to make my sfinae check a friend of my class. 考虑到它是私人的,我必须让我的sfinae检查我班上的朋友。

However, I'm unable to do that, as the following (minimal) code illustrates. 但是,我无法做到这一点,正如下面的(最小)代码所示。 The things I don't want to change : the prototype of class A (f1 and f2 must remain private), the prototypes of class B1 and B2. 我不想改变的事情:A类的原型(f1和f2必须保持私有),B1和B2类的原型。

I understand why the stuff in comments fails (or I think I do), but I don't know how to fix it. 我理解为什么评论中的内容失败(或者我认为我这样做),但我不知道如何修复它。

#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
}

This entire scheme can be simplified a great deal (in coupling too) if you forgo the different names f1 and f2 and do tag dispatch instead: 如果您放弃不同的名称f1f2并执行标记调度,则整个方案可以简化很多(在耦合中):

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>{})); }
};

No friendship, no almost circular dependencies, and you see the exact output you want. 没有友谊,几乎没有循环依赖,你看到你想要的确切输出。 And as icing on the top, adding support for another overload should be fairly easy if the need arises. 并且如果需要的话,作为最重要的结果,添加对另一个重载的支持应该相当容易。

The priority of the overloads is also encoded here (thank Jarod42 for reminding me). 重载的优先级也在这里编码(感谢Jarod42提醒我)。 Since that tags are in an inheritance chain, the second argument priority<1>{} can be provided to either overloads, but in case both are viable, it will favor the closer match. 由于这些标记位于继承链中,因此第二个参数priority<1>{}可以提供给任一重载,但如果两者都可行,则它将有利于更接近的匹配。

A general simple solution to dealing with templated friend functions in templated classes is to just declare the template itself as friend instead of just the overload specific to the current instantiation: 在模板化类中处理模板化友元函数的一般简单解决方案是仅将模板本身声明为朋友,而不仅仅是当前实例化特定的重载:

template<class T>
class A
{
    ...

    template<typename U>
    friend auto sfinae_check(U &u, A<U> &a, int) -> decltype(u.b1(), void()); 

    template<typename U>
    friend auto sfinae_check(U &u, A<U> &a, long) -> decltype(u.b2(), void()); 

    ...
};

Which seems to solve your issue: https://gcc.godbolt.org/z/8UGGZM 这似乎可以解决您的问题: https//gcc.godbolt.org/z/8UGGZM

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM