什么是关于什么参数依赖查找的好解释?很多人也称它为Koenig Lookup.
我最好知道:
我正在阅读C++编程语言,第4版(由Bjarne Stroustrup撰写)关于参数依赖查找.这是引用(26.3.6,Overaggressive ADL):
依赖于参数的查找(通常称为ADL)对于避免冗长非常有用(14.2.4).例如:
Run Code Online (Sandbox Code Playgroud)#include <iostream> int main() { std::cout << "Hello, world" << endl; // OK because of ADL }
如果没有依赖于参数的查找,则
endl
无法找到操纵器.实际上,编译器注意到第一个参数<<
是ostream
定义的std
.因此,寻找endl
在std
和发现它(中<iostream>
).
这是编译器产生的结果(C++ 11模式):
prog.cpp: In function ‘int main()’:
prog.cpp:4:36: error: ‘endl’ was not declared in this scope
std::cout << "Hello, world" << endl;
^
Run Code Online (Sandbox Code Playgroud)
这是编译器或书中的错误.标准说什么?
更新:
我需要澄清一下.我知道正确的答案是使用std::endl
.问题是关于书中的文字.正如Lachlan Easton所说,这不仅仅是一个错字.整段是(可能)错了.如果这本书是由另一位(鲜为人知的)作者写的,我可以接受这种错误,但我(并且仍然)怀疑,因为它是由Bjarne写的.
C++规范的哪一部分限制参数依赖查找在相关命名空间集合中查找函数模板?换句话说,为什么main
下面的最后一次调用无法编译?
namespace ns {
struct foo {};
template<int i> void frob(foo const&) {}
void non_template(foo const&) {}
}
int main() {
ns::foo f;
non_template(f); // This is fine.
frob<0>(f); // This is not.
}
Run Code Online (Sandbox Code Playgroud) 当创建一个按照通常规则播放的自定义容器类(即使用STL算法,使用性能良好的通用代码等)时,在C++ 03中实现迭代器支持和成员开始/结束函数就足够了.
C++ 11引入了两个新概念 - 基于范围的for循环和std :: begin/end.基于范围的for循环理解成员开始/结束函数,因此任何C++ 03容器都支持基于范围的开箱即用.对于算法,推荐的方法(根据Herb Sutter的'Writing modern C++ code')是使用std :: begin而不是member function.
但是,此时我不得不问 - 是否建议调用完全限定的begin()函数(即std :: begin(c))或依赖ADL并调用begin(c)?
在这种特殊情况下,ADL似乎毫无用处 - 因为如果可能的话,std :: begin(c)委托给c.begin(),通常的ADL好处似乎不适用.如果每个人都开始依赖ADL,那么所有自定义容器都必须在其必需的命名空间中实现额外的begin()/ end()自由函数.但是,有几个消息来源似乎暗示对开始/结束的不合格调用是推荐的方式(即https://svn.boost.org/trac/boost/ticket/6357).
那么C++ 11的方式是什么?容器库作者是否应该为其类编写额外的开始/结束函数,以便在没有使用namespace std的情况下支持不合格的开始/结束调用; 或者使用std :: begin;?
前段时间我读了一篇文章解释了参数依赖查找的几个缺陷,但我再也找不到了.它是关于获取您不应该访问的东西或类似的东西.所以我想我会在这里问:ADL的缺陷是什么?
c++ namespaces overload-resolution argument-dependent-lookup
想象一下,我正在写一些容器模板或其他东西.现在是时候专注std::swap
于它了.作为一个好公民,我会通过这样的方式启用ADL:
template <typename T>
void swap(my_template<T>& x, my_template<T>& y) {
using std::swap;
swap(x.something_that_is_a_T, y.something_that_is_a_T);
}
Run Code Online (Sandbox Code Playgroud)
这非常整洁.直到我想添加一个异常规范.我swap
是noexcept
只要调换T
的noexcept
.所以,我会写一些类似的东西:
template <typename T>
void swap(my_template<T>& x, my_template<T>& y)
noexcept(noexcept(swap(std::declval<T>(), std::declval<T>())))
Run Code Online (Sandbox Code Playgroud)
问题是,swap
在那里需要ADL发现swap
或std::swap
.我该如何处理?
以下片段:
#include <memory>
#include <utility>
namespace foo
{
template <typename T>
void swap(T& a, T& b)
{
T tmp = std::move(a);
a = std::move(b);
b = std::move(tmp);
}
struct bar { };
}
void baz()
{
std::unique_ptr<foo::bar> ptr;
ptr.reset();
}
Run Code Online (Sandbox Code Playgroud)
不为我编译:
$ g++ -std=c++11 -c foo.cpp
In file included from /usr/include/c++/5.3.0/memory:81:0,
from foo.cpp:1:
/usr/include/c++/5.3.0/bits/unique_ptr.h: In instantiation of ‘void std::unique_ptr<_Tp, _Dp>::reset(std::unique_ptr<_Tp, _Dp>::pointer) [with _Tp = foo::bar; _Dp = std::default_delete<foo::bar>; std::unique_ptr<_Tp, _Dp>::pointer = foo::bar*]’:
foo.cpp:20:15: required from here
/usr/include/c++/5.3.0/bits/unique_ptr.h:342:6: error: call of …
Run Code Online (Sandbox Code Playgroud) 我只是想知道,为什么有人写这个:
std::iter_swap(i, k);
Run Code Online (Sandbox Code Playgroud)
而不是这个?
std::swap(*i, *k); // saved a few keystrokes!
Run Code Online (Sandbox Code Playgroud)
然后我查看了实现iter_swap
,当然它只使用swap
而不是std::swap
因为我们已经在namespace std
,但无论如何.这引出了我的下一个问题:
为什么有人写这个:
using std::swap;
swap(a, b);
Run Code Online (Sandbox Code Playgroud)
而不是这个?
std::iter_swap(&a, &b); // saved an entire line of code!
Run Code Online (Sandbox Code Playgroud)
我在这里忽略了重要的差异/问题吗?
假设以下代码:
#include <iostream>
using namespace std;
namespace X
{
class A{};
void f(A a){}
void g(int a){}
}
int main()
{
X::A a;
f(a);
g(5);
}
Run Code Online (Sandbox Code Playgroud)
编译代码时,会发生以下编译错误:
main.cpp:在函数'int main()'中:
main.cpp:error:'g'未在此范围内声明
所以函数f
编译得很完美,但事实g
并非如此.怎么样?它们都属于同一名称空间.编译器是否从类型的参数中推断出该函数f
属于X
命名空间X::A
?在这种情况下编译器如何表现?
在您尝试阅读整篇文章之前,请了解:
fameta::counter
类中,以解决一些剩余的怪癖。你可以在 github 上找到它;自从 Filip Roséen 在 2015 年发现/发明了通过友元注入编译时间计数器的黑魔法是在 C++ 中,我一直对这个设备有点着迷,所以当 CWG决定功能必须去时,我很失望,但仍然充满希望可以通过向他们展示一些引人注目的用例来改变他们的想法。
然后,几年前,我决定再看一遍,以便uberswitch es可以嵌套 - 在我看来,这是一个有趣的用例 - 只是发现它不再适用于新版本的可用的编译器,即使问题 2118是(现在仍然是)处于打开状态:代码会编译,但计数器不会增加。
该问题已在 Roséen 的网站和最近的 stackoverflow 上报告:Does C++ support compile-time counters?
我想了解编译器发生了什么变化,使看似仍然有效的 C++ 不再起作用。为此,我在互联网上广泛搜索,寻找有人谈论它,但无济于事。所以我开始尝试并得出了一些结论,我在这里提出这些结论是希望能从这里的知识渊博的人那里得到反馈。
为了清楚起见,我在下面展示了 Roséen 的原始代码。有关其工作原理的说明,请参阅他的网站:
template<int N>
struct flag {
friend constexpr int adl_flag …
Run Code Online (Sandbox Code Playgroud) c++ counter friend-function argument-dependent-lookup constexpr
c++ ×10
c++11 ×4
namespaces ×3
name-lookup ×2
c++-faq ×1
constexpr ×1
containers ×1
counter ×1
gcc ×1
iterator ×1
libstdc++ ×1
noexcept ×1
swap ×1
templates ×1