我遇到了一些看起来像是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
作为替代),但很高兴地编译使用{@ 1}}在下一行。现在,bar
和foo
都通过bar
声明注入到命名空间test
中,因此全局命名空间中都不应该存在。
Q1 我错了,或者这是c ++ 11的新转折,还是gcc行为不端?
当然,如果我只是将指令注入全局命名空间,就可以避免这个问题。但是,如果我将friend
设为模板,
A
gcc再次抱怨。要么(没有#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;
}
指令)“using
未在此范围内声明”(但又愉快地编译foo
,但不建议bar
)或(与test::foo
指令)using
指令点“test::foo
尚未声明”(test::bar
相同)。
Q2 这对我来说就像编译器错误一样,因为无论是否使用using
指令都可以调用using
。或者也许我错过了一些关于C ++的东西?
最后,我尝试将好友定义移到课堂外,如
test::foo
当gcc再次抱怨时,这次声称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;
被使用但从未定义过......所以 Q3 出了什么问题?
对任何子问题欢迎的答案。
答案 0 :(得分:3)
<强> 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:
这看起来像编译器错误,因为无论是否使用using指令都可以调用test :: foo。或者也许我错过了一些关于C ++的东西?
如果你的类成为一个你永远不会实例化的类模板,那么编译器永远不会执行在实例化A<>
时会发生的第二阶段名称查找,所以它永远不会发现其中声明了两个friend
函数。
例如,如果您在 using
声明之前引入了模板的显式实例化,那么您应该会看到一些变化:
template class test::A<int>;
或者,您可以更改A
的定义,使其仅声明,并且不定义两个friend
函数模板,并提供输出这些功能模板的类定义。我想,这就是你真正试图做的事情。但...
<强> Q3:强>
gcc再次抱怨,这次声称使用了void test :: foo(const test :: A&amp;,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;
}
答案 1 :(得分:1)
您的问题和问题的答案称为ADL及其应用时间的规则。它在C ++ 11中并不新鲜,而且它不是GCC的问题。
Q1:您有a
类型的参数test::A
(在第一个示例中),因此ADL(参数相关查找)查找名称空间{{1}中的方法},但仅用于非模板调用。这就是test
(模板调用)未找到且foo<2>
为。
Q2:在第3季后回答,见下文。
Q3: bar
的函数定义未定义您声明为test::foo
的朋友的函数。将其更改为
test::A<T>
Q2:与Q3类似,您可以像这样修复:
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;
Andy已经解释了为什么你的原始例子不起作用。