朋友,模板,命名空间

时间:2018-05-09 20:22:13

标签: c++ templates c++17 friend

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

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

2 个答案:

答案 0 :(得分:8)

在C ++ 20之前,您需要教导编译器bar是模板的名称,以便它知道<启动模板参数列表而不是小于运算符:

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在看到<并且名称查找找不到任何内容时为模板命名,因此您的代码将正常工作

答案 1 :(得分:4)

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

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