友元、模板、命名空间

friend, template, namespace

我想要一个模板化的好友功能。但是,我不知道如何使它在没有模板化功能的情况下以相同的方式工作。 这是一个示例代码

#include <iostream>

namespace ns{
struct Obj {
    friend void foo(Obj){std::cout << "no problem" << std::endl;}

    template<typename T>
    friend void bar(Obj){std::cout << "problem" << std::endl;}
};
}

int main() {
    ns::Obj obj;
    foo(obj); // Compile
    bar<int>(obj); // Not compile
    return 0;
}

在 C++20 之前,您需要告诉编译器 bar 是模板的名称,以便它知道 < 开始一个模板参数列表,而不是 less-比运算符:

template<char> void bar() = delete;

int main() {
    ns::Obj obj;
    foo(obj); // Compile
    bar<int>(obj); // Now compiles too
    return 0;
}

请注意,bar 重载所要做的就是成为一个函数模板。只要签名不至于干扰重载解析,签名就无关紧要; () 是一个不错的选择,因为根据定义我们至少要传递一个参数,因此不带参数的函数模板永远不可行。

或者,您可以重新设计 bar 以从标签参数推导出 T

template<class T>
struct type {};

namespace ns{
struct Obj {    
    // ...

    template<typename T>
    friend void bar(Obj, type<T>) { /* ... */ }
};
}
// ...

bar(obj, type<int>()); // OK

在 C++20 中,编译器会假定 bar 在看到 < 时命名一个模板并且名称查找什么也找不到,因此您的代码将 正常工作.

直接的方法是添加前向声明并使用限定查找定位函数:

namespace ns{
struct Obj;

void foo(Obj);

template<typename T>
void bar(Obj);

struct Obj {
    friend void foo(Obj){std::cout << "no problem" << std::endl;}

    template<typename T>
    friend void bar(Obj){std::cout << "problem " << std::endl;}
};
} // namespace ns

int main() {
    ns::Obj obj;
    ns::foo(obj); // Ok
    ns::bar<int>(obj); // Ok
    return 0;
}