模板类的模板友的问题
problems with template friend of template class
我遇到了c++编译器不一致的情况。在下面的示例代码
中#include <vector>
namespace test {
class A : std::vector<int>
{
template<typename F>
friend void bar(A const&a, F f) { for(auto i:a) f(i); }
template<int K, typename F>
friend void foo(A const&a, F f) { for(auto i:a) if(i&K) f(i); }
};
}
int sum(test::A const&a)
{
int s=0;
foo<2>(a,[&s](int i) { s+=i; } ); // <-- error here
bar (a,[&s](int i) { s+=i; } ); // <-- but not here
return s;
}
gcc (4.7.0, using std=c++11)抱怨"foo
未在此范围内声明"(并建议将test::foo
作为替代),但很高兴在下一行编译bar
的使用。现在,foo
和bar
都通过它们的friend
声明注入到名称空间test
中,所以它们都不应该真正出现在全局名称空间中。
Q1是我错了,还是这是c++11的一个新转折,还是gcc行为不当?
当然,如果我简单地将using指令注入全局命名空间,就可以避免这个问题。但是,如果我将A
作为模板,
#include <vector>
namespace test {
template<typename T>
class A : std::vector<T>
{
template<typename F>
friend void bar(A const&a, F f) { for(auto i:a) f(i); }
template<int K, typename F>
friend void foo(A const&a, F f) { for(auto i:a) if(i&K) f(i); }
};
}
using test::foo; // does not avoid compilation error
using test::bar; // does not avoid compilation error
int sum(test::A<int> const&a)
{
int s=0;
foo<2>(a,[&s](int i) { s+=i; } );
bar (a,[&s](int i) { s+=i; } );
return s;
}
gcc又抱怨了。要么(没有using
指令)"foo
未在此范围内声明"(但再次愉快地编译bar
,尽管不建议test::foo
),要么(使用using
指令)在using
指令的点上"test::foo
未声明"(test::bar
也是如此)。
Q2这看起来像一个编译器错误,因为无论有没有using
指令,我都可以调用test::foo
。或者我可能错过了关于c++的一些东西?
最后,我尝试将友元定义移出类,如
namespace test {
template<typename T>
class A : std::vector<int>
{
template<int K, typename F>
friend void foo(A const&a, F f);
template<typename F>
friend void bar(A const&a, F f) { for(auto i:a) f(i); }
};
template<int K, typename T, typename F>
void foo(A<T> const&a, F f) { for(auto i:a) if(i&K) f(i); }
}
using test::foo;
当gcc再次抱怨时,这次声称使用了void test::foo(const test::A<T>&, F)
但从未定义…那么Q3怎么了?
欢迎回答子问题
Q1:
是我错了,还是这是c++11的一个新转折,还是gcc行为不当?
不,这是正常行为。c++ 11标准第14.8.1/8段:
对于简单的函数名,依赖于参数的查找(3.4.2)即使在函数名在调用范围内不可见。这是因为调用仍然具有函数的语法形式调用(3.4.1)。但是,当使用带有显式模板实参的函数模板时,调用则没有正确的语法形式,除非在调用的地方有一个具有该名称的函数模板可见。如果没有这样的名称可见,则调用在语法上不是格式良好的,依赖参数的查找也不是适用。如果某些这样的名称可见,则应用依赖于参数的查找和附加的函数模板可以在其他名称空间中找到。(例子:
namespace A {
struct B { };
template<int X> void f(B);
}
namespace C {
template<class T> void f(T t);
}
void g(A::B b) {
f<3>(b); // ill-formed: not a function call
A::f<3>(b); // well-formed
C::f<3>(b); // ill-formed; argument dependent lookup
// applies only to unqualified names
using C::f;
f<3>(b); // well-formed because C::f is visible; then
// A::f is found by argument dependent lookup
}
-end example]
Q2:
这看起来像一个编译器错误,因为无论是使用或不使用指令我可以调用test::foo。或者我可能错过了关于c++的一些东西?
如果你的类变成了一个你从未实例化过的类模板,那么编译器将永远不会执行在实例化A<>
时发生的第二阶段名称查找,因此它将永远不会发现其中声明了两个friend
函数。
如果你在 using
声明之前引入了模板的显式实例化,你应该看到事情发生了变化:
template class test::A<int>;
或者,您可以更改A
的定义,使其仅声明而不定义两个friend
函数模板,并为这些函数模板提供类外定义。我猜这就是你想要做的。但是…
第三季:
gcc再次抱怨,这次声称使用了void test::foo(const test::A&, F)但从未定义…那么到底出了什么问题呢?
问题在于您没有将稍后定义的函数声明为友元:注意,您定义的函数有一个额外的参数(T
)。修改你的声明,你会看到程序编译:
namespace test
{
template<typename T>
class A : std::vector<int>
{
template<int K, typename C, typename F>
// ^^^^^^^^^^ (can't use T here, it would shadow
// the class's template parameter)
friend void foo(A<C> const&a, F f);
};
template<int K, typename C, typename F>
void foo(A<C> const&a, F f)
{ for(auto i:a) if(i&K) f(i); }
}
using test::foo; // Just don't remove this, or we will be back in Q1 ;-)
结论:
因此,在所有必要的修改之后,您的程序将是这样的:#include <vector>
namespace test
{
template<typename T>
class A : std::vector<T>
{
template<typename F, typename C>
friend void bar(A<C> const&a, F f);
template<int K, typename F, typename C>
friend void foo(A<C> const&a, F f);
};
template<typename F, typename C>
void bar(A<C> const&a, F f) { for(auto i:a) f(i); }
template<int K, typename F, typename C>
void foo(A<C> const&a, F f) { for(auto i:a) if(i&K) f(i); }
}
using test::foo;
using test::bar;
int sum(test::A<int> const& a)
{
int s=0;
foo<2>(a,[&s](int i) { s+=i; } );
bar (a,[&s](int i) { s+=i; } );
return s;
}
你的问题和问题的答案被称为ADL以及何时应用它的规则。这在c++ 11中不是新的,在GCC中也不是问题。
Q1:您有一个类型为test::A
的参数a
(在第一个示例中),因此ADL(参数依赖查找)在名称空间test
中查找方法,但仅用于非模板调用。这就是为什么没有找到foo<2>
(一个模板调用),而bar
是。
Q2: Q3后回答,见下文
Q3:您的test::foo
函数定义没有定义您声明为test::A<T>
友元的函数。改为
namespace test
{
template<typename T>
class A;
template<int K, typename F,typename T>
void foo(A<T> const&a, F f);
template<typename T>
class A : std::vector<int>
{
template<int K, typename F,typename U>
friend void foo(A<U> const&a, F f);
template<typename F>
friend void bar(A const&a, F f) { for(auto i:a) f(i); }
};
template<int K, typename F,typename T>
void foo(A<T> const&a, F f) { for(auto i:a) if(i&K) f(i); }
}
using test::foo;
Q2:与Q3类似,您可以这样修复它:
#include <vector>
namespace test {
template<typename T>
class A;
template<typename F,typename T>
void bar(A<T> const&a, F f);
template<int K, typename F,typename T>
void foo(A<T> const&a, F f);
template<typename T>
class A : std::vector<T>
{
template<typename F,typename U>
friend void bar(A<U> const&a, F f);
template<int K, typename F,typename U>
friend void foo(A<U> const&a, F f);
};
template<typename F,typename U>
void bar(A<U> const&a, F f) { for(auto i:a) f(i); }
template<int K, typename F,typename U>
void foo(A<U> const&a, F f) { for(auto i:a) if(i&K) f(i); }
}
using test::foo;
using test::bar;
int sum(test::A<int> const&a)
{
int s=0;
foo<2>(a,[&s](int i) { s+=i; } );
bar (a,[&s](int i) { s+=i; } );
return s;
}
Andy已经解释了为什么你原来的例子不起作用
- 警告处理为错误这里有什么问题
- 最小硬币更换问题(自上而下方法)
- 为"adjacent"变量赋值时出现问题
- 我的神经网络不起作用 [XOR 问题]
- 在Ubuntu 16.04上安装Cilk时出现问题
- C++我的数学有什么问题,为什么我的代码不能正确循环
- 编译包含字符串的代码时遇到问题
- Project Euler问题4的错误解决方案
- 问题:什么是QAbstractItemView::NoEditTriggers的反面
- 在编译C++代码(具有dlib和opencv)到WASM时面临问题
- 在进程中对同一管道进行读取和写入时C++管道出现问题
- 静态数据成员的问题-修复链接错误会导致编译器错误
- C++ 雷神库 - 使用资源加载器类时出现问题(不命名类型)
- 一个关于在C++中重载布尔运算符的问题
- 首要问题的答案让值班员搞错了
- setlocale的C++土耳其字符串问题
- 如何重构类层次结构以避免菱形问题
- 基于boost的程序的静态链接——zlib问题
- C++格式化输出问题
- 使用mongocxx驱动程序时包含头文件问题