惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

小众软件
小众软件
N
News and Events Feed by Topic
A
About on SuperTechFans
aimingoo的专栏
aimingoo的专栏
The Cloudflare Blog
H
Heimdal Security Blog
Schneier on Security
Schneier on Security
Engineering at Meta
Engineering at Meta
Google Online Security Blog
Google Online Security Blog
宝玉的分享
宝玉的分享
AI
AI
The GitHub Blog
The GitHub Blog
MongoDB | Blog
MongoDB | Blog
www.infosecurity-magazine.com
www.infosecurity-magazine.com
The Last Watchdog
The Last Watchdog
T
Troy Hunt's Blog
S
Security @ Cisco Blogs
H
Hacker News: Front Page
F
Fortinet All Blogs
博客园_首页
S
Secure Thoughts
N
News and Events Feed by Topic
P
Proofpoint News Feed
Microsoft Azure Blog
Microsoft Azure Blog
I
InfoQ
Spread Privacy
Spread Privacy
Hacker News - Newest:
Hacker News - Newest: "LLM"
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
C
Check Point Blog
Hugging Face - Blog
Hugging Face - Blog
Hacker News: Ask HN
Hacker News: Ask HN
C
CXSECURITY Database RSS Feed - CXSecurity.com
酷 壳 – CoolShell
酷 壳 – CoolShell
Stack Overflow Blog
Stack Overflow Blog
L
LINUX DO - 最新话题
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
S
Schneier on Security
Know Your Adversary
Know Your Adversary
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Scott Helme
Scott Helme
P
Privacy & Cybersecurity Law Blog
S
Securelist
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
O
OpenAI News
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
PCI Perspectives
PCI Perspectives
L
LangChain Blog
雷峰网
雷峰网
Security Archives - TechRepublic
Security Archives - TechRepublic
V2EX - 技术
V2EX - 技术

核桃的炼金工坊

2023 韩国游记 C++23: Flat Containers Stateful Metaprogramming 推し、燃ゆ Customization Point Object 2020 总结 搞了个 C++ 构建系统 软件设计哲学(NOTE) Paxos Note 关于 cpp 可见性的黑魔法后门 一个关于 private member function detect 的 SFINAE 模板 User-defined conversion and Copy elision VIM and Latex Compare Between CRTP and Virtual Interface in C++ Compile Time Reflection in C++11 C++11内存模型 在C++17中的部分新特性 Const Reference of Pointer
Deducing This
Hawtian Wang · 2023-09-17 · via 核桃的炼金工坊

A new way of declaring non-static member functions that will allow for deducing the type and value category of the class instance parameter while still being invocable with regular member function syntax.

简单的说,就是让非静态成员函数的 this 参数成为一个显式参数。而不破坏以前的调用成员函数的语法。

The new syntax is to use an explicit this-annotated parameter.

一个非静态成员函数可以把它的第一个参数成为一个显式对象参数 (explicit object parameter),用一个关键字前缀 this 来表示。 这个参数可以像其他参数一样,使用 cv-ref- 限定符。那么当然也可以用模板。

struct Foo {
    void foo(this const Foo&);
    void foo(this volatile Foo&&);
    template<typename Self>
    void bar(this Self self);
    void barbar(this auto self);
};

非静态成员函数,隐式决定对象类型和显式对象参数类型的表示:

Implicit objectExplicit Object Parameter
void foo() &;void foo(this X&);
void foo() const &;void foo(this const X&);
void foo() &&;void foo(this X&&);

在调用一个这样的成员函数的时候,这个对象会作为第一个参数,其他参数会依次推后一个。 ( 比如括号里的第一个参数,实际是声明里的第二个参数。 )

考虑下面两个不同的函数声明,他们的类型分别为:

struct Y {
    int f(int, int) const&;
    int g(this Y const&, int, int);
};
  • &Y::f is int(Y::*)(int, int) const&
  • &Y::g is int(*)(const Y&, int, int)

也就是说,带有 explicit object parameter 的函数可以理解成被视作静态成员函数的。 所以静态成员函数也不能有 explicit object parameter

实际上带有 explicit object parameter 不同与非静态成员函数和静态成员函数, 是另一种成员函数类型。

They’re like semi-static member functions. We’ll call them explicit object member functions due to them having an explicit object parameter.

非静态EOMF静态
Requires/uses an object argumentYesYesNo
隐式声明了 thisYesNoNo
可以声明重载运算符YesYesNo
&C::f 的类型成员函数指针函数指针函数指针
虚函数?YesMaybeNo
auto f = x.f;NoNoYes
名字可以退化成函数指针NoNoYes

2.2 Candidate Functions

有了 Deducing this 之后,新加入了带有 explicit object parameter 的 candidates。

The only change in how we look up candidate functions is in the case of an explicit object parameter, where the argument list is shifted by one. The first listed parameter is bound to the object argument, and the second listed parameter corresponds to the first argument of the call expression.

由于带 explicit object parameter 的函数会被视作静态函数,并且在参与重载决议的时候, 参数被视作从第二个参数开始。所以下面这两个函数定义是冲突的:

static void foo();
void foo(this X const&);

类似 std::optional<T> 的例子:

template<typename T>
struct Optional {
    auto value() const & {
        if (has_value()) {
            return value_;
        }
        throw std::bad_optional_access();
    }
    auto value() const&& {
        if (has_value()) {
            return std::move(value_);
        }
        throw std::bad_optional_access();
    }
    auto value() && {
        if (has_value()) {
            return std::move(value_);
        }
        throw std::bad_optional_access();
    }
    auto value() & {
        if (has_value()) {
            return value_;
        }
        throw std::bad_optional_access();
    }
};
template<typename T>
struct Optional {
    auto value(this auto&& self) {
        if (self.has_value()) {
            return std::forward_like<decltype(self)>(self.value_);
        }
        throw std::bad_optional_access();
    }
};

lambda 一直都有个问题是递归比较困难,因为没法拿到 lambda 函数自己这个对象。 所以一般都是用些 trick 的方法。要不就是转成一个 std::function 的对象存起来, 要不就是要在每次调用的时候,都把自己作为一个参数传给自己,像这样:

std::function<int(int)> fib;
fib = [&fib](int i) {
    if (i < 2) return 1;
    return fib(i - 1) + fib(i - 2);
};

auto fib2 = [](auto& fib, int i) {
    if (i < 2) return 1;
    return fib(i - 1) + fib(i - 2);
};

std::cout << fib2(fib2, 10);
auto fib = [](this auto self, int i) {
    if (i < 2) return 1;
    return self(i - 1) + self(i - 2);
};

Update 2023.11.8

这个递归甚至可以被用在 std::visit 里面:

struct Leaf {};
struct Node;
using Tree = std::variant<Leaf, Node*>;
struct Node {
  Tree left, right;
};

template <typename... Ts>
struct overload : Ts... { using Ts::operator()...; }

int countLeaves(const Tree& tree) {
  return std::visit(overload{
      [] (const Leaf&) { return 1; },
      [] (this const auto& self, const Node* node) -> int {
        return visit(self, node->left) + visit(self, node->right);
      }
    },
    tree);
}

CRTP 一个重要的部分,就是要在基类里得到子类的类型,这个一般都是把基类变成一个模板类, 然后在继承的时候把子类类型传进去得到的。有 Deducing this 之后,explicit object member function 的 explicit object parameter 是模板的话,就可以在调用的时候得到实际子类的类型。

template<typename D>
struct CRTP {
    void foo() {
        static_cast<D*>(this)->fooImpl();
    }
};
struct CRTP {
    void foo(this auto& self) {
        self.fooImpl();
    }
};
struct Builder {
    Builder name(this Builder self, auto value) { self.name_ = std::move(value); return self; }
    Builder password(this Builder self, auto value) { self.password_ = std::move(value); return self; }
};

对于像 std::string_view 这类的小对象,传值是个比传引用更快,所以应该更多传值而不是传引用, 对于这类的对象。但是在以前一个函数调用会把 *this 作为一个参数传着。对于这种对象, 把 *this 变成 Self 显然是个很大的性能优化。

struct StringView {
    auto length(this Stringview self) { return self.length; }
};

  1. C++ Draft: Duducing this