我想知道为什么下面的代码会编译。
#include <iostream>
template<class T>
void print(T t) {
std::cout << t;
}
namespace ns {
struct A {};
}
std::ostream& operator<<(std::ostream& out, ns::A) {
return out << "hi!";
}
int main() {
print(ns::A{});
}
Run Code Online (Sandbox Code Playgroud)
我的印象是,在实例化点不合格的依赖名称仅通过 ADL 查找- 这不应该考虑全局名称空间。我错了吗?
c++ templates language-lawyer name-lookup argument-dependent-lookup
从 C++20 开始,在[namespace.std]/7 中引入了定制点的概念:
除了在命名空间 std 或命名空间 std 内的命名空间中,程序可以为指定为自定义点的任何库函数模板提供重载,前提是 (a) 重载的声明依赖于至少一个用户定义的类型和 (b ) 重载满足自定义点的标准库要求。[ 注意:这允许对自定义点的(限定或非限定)调用为给定参数调用最合适的重载。— 尾注 ]
注释部分(注意强调的词“合格”)是否意味着std::f将自动调用最合适的重载fifstd::f是自定义点?
一个真实的例子是std::swap,这是一个指定的定制点。这是否意味着从 C++20 开始,我们可以std::swap(a, b)直接编写而不是using std::swap; swap(a, b);?
c++ overloading c++-standard-library argument-dependent-lookup c++20
我想在函数调用中查找期间考虑非类型模板参数的命名空间中的函数。
例如:
template<auto& N>
struct test_nontype {
int call() {
return f(*this); // error: 'f' was not declared in this scope
}
};
template<typename T>
struct test_type {
int call() {
return f(*this); // ok, f is found in ns via ADL
}
};
namespace ns {
static struct type_in_ns {} value;
int f(test_nontype<value>) {
return 3;
}
int f(test_type<type_in_ns&>) {
return -3;
}
}
int main() {
int x = test_type<ns::type_in_ns&>{}.call();
return x + test_nontype<ns::value>{}.call();
}
Run Code Online (Sandbox Code Playgroud)
对于类型,它工作正常,但最后一行无法编译,因为int ns::f(test_nontype<ns::value>) …
考虑
// https://godbolt.org/z/z5M9b9jzx
#include <memory>
#include <cassert>
struct B {};
struct D : B {};
int main() {
std::shared_ptr<B> b = std::make_shared<D>();
auto d = static_pointer_cast<D>(b);
assert(d);
}
Run Code Online (Sandbox Code Playgroud)
我本来希望static_pointer_cast对解析为的不合格调用std::static_pointer_cast,因为b作为std::shared_ptr,应该引入namespace std使用 ADL。
为什么不呢?我需要std::shared_pointer_cast明确地编写以使其工作。
c++ shared-ptr argument-dependent-lookup unqualified-name static-pointer-cast
很久以前我注意到在Visual C++ 10中,当至少有一个参数是lambda时ADL失败了.
std::vector<float> vec;
for_each(begin(vec), end(vec), [](float) {});
Run Code Online (Sandbox Code Playgroud)
以上无法在VC++ 10和11(beta)上编译(通过ADL找到开始和结束).当我将lambda函数转换为常规自由函数时,事情就像预期的那样工作.
我曾经在Herb Sutters博客上问了一次,并且还阅读了msdn connect上的一些帖子,通常的答案是:这是一个错误,我们还没有实现lambdas的最新标准,但当时 - 这是很容易理解的.事情尚未成熟.在MS连接上也有令人不安的评论,即下一版本即vc 11将无法解决这个问题.
我的问题是,是这样的代码预期的C++ 11个标准下工作?我无法理解这一点.当我使用lambdas时,我是否真的必须使用std ::为我的for_each和其他算法添加前缀?我不知何故怀疑在vc ++ 11发布后这种行为不会改变.
在一个内部数据类型的情况下,让函数的返回类型与从头auto foo(T f)调用时相同:sin(f)cmathf
template <typename T>
auto foo(T f) -> decltype(sin(f))
{
using std::sin;
return sin(f);
}
Run Code Online (Sandbox Code Playgroud)
这已破了.该sin(f)范围内decltype没有抬头之内std,因此只C变种sin(double)被发现,它的返回类型为double.以下程序演示了:
#include <cmath>
#include <iostream>
#include <typeinfo>
namespace meh {
struct Nanometer {};
struct SinfulNanometer {};
SinfulNanometer sin(Nanometer) { return SinfulNanometer(); }
}
template <typename T>
auto foo(T f) -> decltype(sin(f))
{
using std::sin;
std::cout << typeid(decltype(sin(f))).name() << '\n';
}
int main () {
std::cout …Run Code Online (Sandbox Code Playgroud) 在friend以下功能不受普通查找(§7.3.1.2/ 3)发现,但是由ADL(§3.4.2/ 4第二个项目符号点)发现,所以代码编译和正常执行(活例子).但是该函数f未在任何名称空间中声明.例如,如果您尝试f(x);通过任何这些调用替换调用::f(x);,A::f(x);或者A::X::f(x);代码将无法编译.哪个命名空间确实包含此友元函数的声明?标准是否对此有所说明?
#include <iostream>
namespace A {
class X {
int i;
friend void f(X x) { std::cout << x.i << '\n'; }
public:
X():i(101){}
};
}
int main()
{
A::X x;
f(x);
}
Run Code Online (Sandbox Code Playgroud) 在测试中遇到以下任务:
#include <iostream> using namespace std;
template<typename T> void adl(T) { cout << "T"; }
struct S { };
template<typename T> void call_adl(T t) { adl(S()); adl(t); }
void adl(S) { cout << "S"; }
int main () { call_adl(S()); }
Run Code Online (Sandbox Code Playgroud)
问题是将调用哪些函数.还有一种解释是,在模板定义时解析不依赖于模板参数的函数的名称,而在知道模板参数时解析依赖于模板参数的函数的名称.嗯,这些"时代"之间有什么区别?
我试图在使用参数依赖查找(ADL)的函数中使用std :: initializer_list作为参数.但是我没有让它工作,我不明白为什么.以下是最小的失败示例:
#include <initializer_list>
#include <iostream>
class Foo {
public:
inline friend void bar(std::initializer_list<Foo> v) {
std::cout << "size = " << v.size() << std::endl;
}
};
void baz(std::initializer_list<Foo> v) {
std::cout << "size = " << v.size() << std::endl;
}
int main(){
Foo a;
//bar({a,a}); // error: use of undeclared identifier 'bar'
baz({a,a}); // works
return 0;
}
Run Code Online (Sandbox Code Playgroud)
如上所示,等效的全局函数可以正常工作.为什么以上不起作用?
我在OS X 10.10上使用了clang.
#include <iostream>
#include <string>
class X {};
namespace N
{
std::string to_string(X)
{
return "foo";
}
void foo()
{
//using std::to_string; // will break the build if uncommented...
//using N::to_string; // ...unless this is uncommented as well
std::cout << to_string(X()) << std::endl;
}
}
int main()
{
N::foo();
return 0;
}
Run Code Online (Sandbox Code Playgroud)
要么我偶然发现了许多我不掌握的C++ arcana中的一个,或者我在这里遗漏了一些明显的东西.
如何using std::to_string在非限定查找期间将可用的名称集合减少到只能通过ADL访问的名称?虽然我想这个问题可能是to_string(X)在不同的命名空间比声明X,我不禁注意到,如果没有using std::to_string,N::to_string(X)仅仅是可供N::foo()利用"正常"的,直观的查找规则,我习惯了.
c++ ×10
c++11 ×4
name-lookup ×2
namespaces ×2
templates ×2
c++20 ×1
decltype ×1
lambda ×1
lookup ×1
overloading ×1
shared-ptr ×1
visual-c++ ×1