如此处所述,C++11 样式 SFINAE 和模板实例化类成员函数上的函数可见性掩盖了自由函数。使用完全限定名称通常有效,但是我很难使用其他内嵌声明的类的友元函数。考虑以下示例:
namespace N {
struct C {
friend int f(const C& c) {
return 1;
}
friend int g(const C& c) {
return 2;
}
};
struct D {
void f() {
g(C{}); // ADL finds this
::N::f(C{}); // not found dispite full qualification
}
};
}
Run Code Online (Sandbox Code Playgroud)
我想我明白问题是什么,正如这里描述的内联友元函数的范围是什么?内联友元函数通常使用 ADL 找到,并且在封闭的命名空间中并不真正可见。
所以我的问题是我应该如何更改我的代码以使其工作(除了重命名 f 之一)?
如果我有一个普通的类,我可以在类中“注入”一个非自由的友元函数。(除其他外,只能由 ADL 找到)。
情况1:
class A{
double p_;
friend double f(A const& a){return a.p_;}
};
Run Code Online (Sandbox Code Playgroud)
如果这是一个模板类,我可以这样做:
案例2:
template<class T>
class A{
double p_;
friend double f(A const& a){return a.p_;} // apparently A const& is a synomyn for A<T> const&
};
Run Code Online (Sandbox Code Playgroud)
现在假设我需要f根据稍后需要定义的类来实现。我在这种情况下尝试这样做:
案例3:
template<class T>
class A{
double p_;
friend double f(A const& a);
};
...
Run Code Online (Sandbox Code Playgroud)
这已经给出了一个警告:“警告:朋友声明 'double f(const A&)' 声明了一个非模板函数 [-Wnon-template-friend]”。
按照编译器的建议,我可以这样做:
template<class T> class A;
template<class T> double f(A<T> const& a);
template<class T>
class A{
double p_; …Run Code Online (Sandbox Code Playgroud) c++ forward-declaration friend-function argument-dependent-lookup c++11
考虑以下:
struct Base {
void foo();
};
template <class T>
struct Derived : Base {
void bar() {
this->foo();
}
};
Run Code Online (Sandbox Code Playgroud)
通常,我们解释了this->使它foo()成为从属名称的原因,因此其查找被推迟到第二阶段,即模板实例化点。
但是第二阶段查找仅调用不考虑成员函数的ADL,对吗?
我很感谢任何指向Standard段落的指针,这些段落解释了为什么上面的代码会编译。
c++ templates language-lawyer name-lookup argument-dependent-lookup
考虑以下示例:
namespace N {
template<class>
struct C { };
template<int, class T>
void foo(C<T>);
}
template<class T>
void bar(N::C<T> c) {
foo<0>(c);
}
int main() {
N::C<void> c;
bar(c);
}
Run Code Online (Sandbox Code Playgroud)
GCC和Clang的失败编译C ++下17个标准(本规范-Werror),因为(根据我的理解)在C ++ 17 ADL不明确时,模板参数不工作<...>存在(除非名称已确定为模板名称),因此foo是未找到的非依赖名称。
在 C++20 中,ADL 规则发生了变化,显式模板参数不会阻止 ADL。现在它似乎foo变成了一个应该可以通过 ADL 解析的依赖名称。但是,GCC 和 Clang 对这段代码的有效性有不同的看法。CLang 编译它没有错误,但 GCC (10.2, -std=c++2a) 抱怨:
error: 'foo' was not declared in this scope; did you mean 'N::foo'?
在 C++17 模式下,Clang 产生以下警告:
warning: use of function template …
c++ templates language-lawyer argument-dependent-lookup c++20
当我用 C++ 编程时,using namespace std;我通常倾向于使用std::前缀组件,例如等std::cout,而不是编写std::cin。但是后来我遇到了 ADL 以及为什么应该使用using std::swap;。
标准库(在 std 内)的许多组件以不合格的方式调用交换,以允许调用非基本类型的自定义重载,而不是此通用版本:在与其所属类型相同的命名空间中声明的交换的自定义重载提供通过对此通用版本进行参数相关的查找来选择。
但在所有有关 ADL 的资料中,他们只提到std::swap. 还有其他类似的功能在使用时需要注意吗?或者对于所有其他功能,我应该使用完全限定名称吗?或者我应该为中的每个函数使用非限定名称std::?
编辑:(在表达我最初的问题时,我不清楚。这是我在写问题时的确切意图。)
C++ 标准库中是否还有其他函数是基于 ADL 的自定义的热门候选函数,类似于std::swap,因此当我使用它们时,我必须谨慎使用using std::foo; foo();表单而不是std::foo();直接调用?
c++ std qualified-name argument-dependent-lookup unqualified-name
我一直在阅读Josuttis模板书,我一直试图把我的脑袋放在ADL周围.他说"ADL通过查找名称空间和类中的名称"与"调用参数的类型"相关联.我只是想看看它是如何工作的,在课堂上查找名字.我在下面举了一个测试例子.我看到它如何在命名空间中查找名称.
class bryan_ns {
public:
class bryan {
public:
enum E { e1 };
static void bryan_test() { std::cout << __PRETTY_FUNCTION__ << std::endl; }
};
void f(bryan::E) { std::cout << __PRETTY_FUNCTION__ << std::endl; }
};
void f(int)
{
std::cout << "::f(int) called\n";
}
int main()
{
f(bryan_ns::bryan::e1); // calls ::f(int)
}
Run Code Online (Sandbox Code Playgroud)
但是,如果我将bryan_ns更改为如此命名空间:
namespace bryan_ns {
public:
class bryan {
public:
enum E { e1 };
static void bryan_test() { std::cout << __PRETTY_FUNCTION__ << std::endl; }
};
void f(bryan::E) { std::cout …Run Code Online (Sandbox Code Playgroud) 考虑一下代码:
#include <iostream>
#include <algorithm> // std::swap C++98
#include <utility> // std::swap C++11
namespace A
{
template<typename T>
struct Foo {};
template<typename T>
void swap(Foo<T> &lhs, Foo<T> &rhs)
{
std::cout << "A::swap<T>" << std::endl;
}
} /* end namespace A */
namespace std // we explicitly specialize std::swap here
{
template<> // explicit template specialization for std::swap<int>
void swap(A::Foo<int> &lhs, A::Foo<int> &rhs)
noexcept
(is_nothrow_move_constructible<A::Foo<int>>::value && is_nothrow_move_assignable<A::Foo<int>>::value)
{
std::cout << "std::swap<int>" << std::endl;
}
} /* end namespace std */
int …Run Code Online (Sandbox Code Playgroud) #include <iostream>
struct H
{
void swap(H &rhs);
};
void swap(H &, H &)
{
std::cout << "swap(H &t1, H &t2)" << std::endl;
}
void H::swap(H &rhs)
{
using std::swap;
swap(*this, rhs);
}
int main(void)
{
H a;
H b;
a.swap(b);
}
Run Code Online (Sandbox Code Playgroud)
这就是结果:
swap(H &t1, H &t2)
Run Code Online (Sandbox Code Playgroud)
在上面的代码中,我尝试定义一个交换函数H.在函数中void H::swap(H &rhs),我使用using声明使名称std :: swap可见.如果没有using声明,则无法编译代码,因为类中没有可用的带有两个参数的交换函数H.
我在这里有一个问题.在我看来,在我使用using声明之后 - using std::swap它只是使std :: swap - STL中的模板函数可见.所以我认为应该调用STL中的交换H::swap().但结果显示,void swap(H &t1, H &t2)而是调用了它.
所以这是我的问题:
H::swap?考虑http://en.cppreference.com/w/cpp/language/adl中描述的此示例:
namespace A {
struct X;
struct Y;
void f(int);
void g(X);
}
namespace B {
void f(int i) {
f(i); // calls B::f (endless recursion)
}
void g(A::X x) {
g(x); // Error: ambiguous between B::g (ordinary lookup)
// and A::g (argument-dependent lookup)
}
void h(A::Y y) {
h(y); // calls B::h (endless recursion): ADL examines the A namespace
// but finds no A::h, so only B::h from ordinary lookup is used
}
}
Run Code Online (Sandbox Code Playgroud)
我想知道为什么出现歧义,因为如果不考虑ADL规则
"由通常的非限定查找生成的查找集包含以下任何内容".
这里B …
我有一个看起来或多或少像这样的功能:
template<class C> auto f(C const& c) -> decltype(begin(c)){
using std::begin;
return begin(c);
}
Run Code Online (Sandbox Code Playgroud)
函数的主体利用" using和使用"成语和
感谢decltypeSFINAE,如果返回类型无效.
然而,它一般来说并不完美,因为我无法告诉它decltype有using std声明begin.
template<class C> auto f(C const& c) -> decltype(std::begin(c))
Run Code Online (Sandbox Code Playgroud)
也会不一致,例如何时decltype(c)和begin属于不同的命名空间.
周围有路吗?
理想情况下,我想要类似的东西
template<class C> auto f(C const& c) -> decltype(using std::begin; begin(c))
Run Code Online (Sandbox Code Playgroud)
我认为lambda原则上可以工作
template<class C> auto f(C const& c) -> decltype([&]{using std::begin; return begin(c)})
Run Code Online (Sandbox Code Playgroud)
但是内部禁止使用lambdas decltype.
在GCC中有一个有趣的语言扩展("表达式语句")是有希望的,但是它不能在函数体外工作(与未评估的上下文中不允许使用lambda). 否则它将是一个解决方案.
template<class C> auto g(C const& c)
->decltype(({using std::begin; begin(c);})){ …Run Code Online (Sandbox Code Playgroud) c++ decltype using-declaration argument-dependent-lookup c++11