8

我目前正在阅读“Effective C++”,其中有一章包含与此类似的代码:

template <typename T>
class Num {
public:
  Num(int n) { ... }
};

template <typename T>
Num<T> operator*(const Num<T>& lhs, const Num<T>& rhs) { ... }

Num<int> n = 5 * Num<int>(10);

这本书说这不起作用(实际上它不起作用),因为您不能期望编译器使用隐式类型转换来专门化模板。

作为一种解决方案,建议使用“friend”语法在类中定义函数。

//It works
template <typename T>
class Num {
public:
  Num(int n) { ... }

  friend 
  Num operator*(const Num& lhs, const Num& rhs) { ... }
};

Num<int> n = 5 * Num<int>(10);

并且本书建议每当我需要隐式转换为模板类类型时使用这个朋友声明。这一切似乎都说得通。

但是,为什么我不能使用通用函数而不是运算符获得相同的示例?

template <typename T>
class Num {
public:
  Num(int n) { ... }

  friend 
  void doFoo(const Num& lhs) { ... }
};

doFoo(5);

这次编译器抱怨他根本找不到任何“doFoo”。如果我在类之外声明 doFoo,我会得到合理的不匹配类型错误。似乎“朋友……”部分只是被忽略了。

那我的理解有问题吗?在这种情况下,函数和运算符有什么区别?

4

3 回答 3

3

原因是这里

doFoo(5);

foo给定int参数,编译器无法找到。这相当于像这样调用您的朋友运算符:

Num<int> n = 5 * 10;

这将“起作用”,但不是通过调用类中的friend operator*定义Num,而是通过调用整数的内置operator*函数,然后使用来自Num转换构造函数的隐式转换。

于 2012-11-19T10:35:17.867 回答
1

核心问题是查找。声明提供friend了命名空间级别函数的声明,但该声明仅在与其友好的类中可用。在本书提供的示例中,这不是问题:该函数接受两个封闭类型的参数,只要其中一个是封闭类型,Argument Dependent Lookup 将查看类的定义并找到运算符。在您的情况下,情况并非如此,因为只有一个参数并且需要转换,编译器不会查看类的定义。

请注意,这与模板和转换无关:

class A {
   friend void f( int ) {}
   friend void g( int, A ) {}
};
int main() {
   f(5);           // Error: lookup cannot find 'f' declared *only* inside A
   g(5,A());       // Ok, one argument is 'A', lookup will find the function
}

在上面的例子中,没有涉及模板,您可能会在命名空间级别添加一个声明来修复它,但这并不是模板类的真正选择。

class A {
   friend void f() { std::cout << "inside A\n"; }
};
void f(int);     // only declaration
int main() {
   f(5);         // "inside A"
}

这不能用于模板(以及所有实例化类型),因为友元声明是非模板函数的声明。尽管您可以只是为了测试而使用代码:

template <typename T>
struct Num {
   Num(int x) ...
   friend void f( Num const & );
};
Num<int> f(Num<int> const &);    // only declaration
int main() {
   f(5);
}
于 2012-11-19T12:51:19.257 回答
0

是的,这些代码编译器不知道如何使用它。喜欢 doFoo(5)

编译器不知道 5 是 int

于 2012-11-19T10:55:34.153 回答