[英]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,使用std = c ++ 11)抱怨“ foo
未在此范围内声明”(并建议将test::foo
作为替代),但很高兴地编译下一行中bar
的用法。 现在, foo
和bar
都通过他们的friend
声明注入到命名空间test
,因此它们都不应该存在于全局命名空间中。
Q1我错了,或者这是c ++ 11的新转折,还是gcc行为不端?
当然,如果我只是使用指令注入全局命名空间,就可以避免这个问题。 但是,如果我制作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
指令)“ test::foo
尚未声明“(和test::bar
相同)在using
指令处。
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
}
- 末端的例子]
Q2:
这看起来像编译器错误,因为使用或不使用using指令都可以调用test :: foo。 或者也许我错过了一些关于C ++的东西?
如果您的类成为您从未实例化的类模板 ,那么编译器将永远不会执行在实例化A<>
时发生的第二阶段名称查找,因此它将永远不会发现在其中声明了两个friend
函数。
例如,如果您在using
声明之前引入了模板的显式实例化,那么您应该看到更改的内容:
template class test::A<int>;
或者,您可以只更改A
的定义,使其仅声明和不定义两个friend
函数模板,并为这些函数模板提供类外定义。 我想,这就是你真正试图做的事情。 但...
Q3:
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;
}
安迪已经解释了为什么你的原始例子不起作用。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.