如果隐式“__return_inherited_this”可用,会出现哪些C++陷阱?

cbeh67ev  于 2023-11-19  发布在  其他
关注(0)|答案(2)|浏览(83)

我(可能)需要为我的C编译器创建一个扩展,但我必须首先确保这个想法不会在设计上失败。
让我们从头开始,在C
的世界里,众所周知,在离开一些函数时返回对*this对象的引用可能是聪明的。这允许链接调用,这通常对于运算符重载很方便,尽管它也可能对其他常规成员函数有用。然而,继承机制有一些限制:

struct Cat {

    Cat &chain_me() {return *this;}

};

struct Nyan : Cat {

    Nyan &chain_me_more() {return *this;}

};

int main() {

    // Example of chained functions that works
    Nyan().chain_me_more().chain_me();

    // Example of chained functions that doesn't work
    Nyan().chain_me().chain_me_more();

}

字符串
在这个例子中,最后一行是无效的,因为函数Nyan::chain_me_more()不能在Cat::chain_me()之后被链调用,因为它返回了一个对对象Nyan的引用,类型为Cat
为了避免这个问题,提供类的开发人员必须为每个派生类中涉及的每个父函数编写一个特定的重定向器。因此在上面的示例中,必须在Nyan的声明中添加以下行:

// Redirector for "Cat::chain_me()" in "Nyan"
Nyan &chain_me() {return static_cast<Nyan&>(Cat::chain_me());}


当处理一组更复杂的类(声明了许多函数)时,这是一项非常繁琐的任务,并且由于缺少一个重定向器进行修改的风险,因此生成的代码以后很难更新。
在基类中编写函数作为模板,接受参数typename T并返回static_cast<T&>(*this)是行不通的,我也没有找到一种方法来创建一个完全模板化的类来继承,以提供预期的效果。(尽管如此,我不是一个模板天才,所以我可能错过了一些魔术技巧.)
事实上,像__return_inherited_this这样的关键字作为C++的扩展可能是一个非常简单的解决方案,因为Cat类只需要声明如下内容:

chain_me() {__return_inherited_this;}


这个关键字会做它所说的,函数仍然应该返回Cat&,但它可能会自动转换为派生的Nyan&,这取决于它是如何被调用的,这就是为什么我没有放一个返回类型。这是从构造函数语法中得到的启发,它声明没有返回值,但实际上表现得像一个引用。
事实上,事情可能更简单。不需要关键字,因为缺少返回类型已经表明了提供特殊引用的意图:

chain_me() {}


根据实现,这也可能与编译成ASM的自然方式相匹配。当thiscall约定为“this”指针使用寄存器时,字节码只需保留其值,并且函数调用可以直接链接,除了将其他参数传递到堆栈或寄存器之外,不需要做任何其他事情。因此,编译后的程序集和C++源代码不需要做任何事情来触发该功能,这对我来说似乎是一致的。
下面是一个使用这种扩展思想的代码集:

struct Cat {

    chain_me() {}

};

struct Nyan : Cat {

    chain_me_more() {}

};

int main() {

    // Here "chain_me" is called from a Nyan, so it returns a Nyan& and our previous problem is solved
    Nyan nyan = Nyan().chain_me().chain_me_more();

    // Here "chain_me" is called from a Cat, so it returns a "Cat&"
    Cat cat = Cat().chain_me();

    // For obvious reasons, the magic cannot and must not apply to polymorphism
    Cat* ptr = &nyan;

    // Here "chain_me" is called from a Cat*, so it returns a Cat& and cannot call "chain_me_more()", the compilation fails
    ptr->chain_me().chain_me_more();

}


我以前对这个概念做过一些测试,但只是用丑陋的脚本将源代码转换为标准C++,我不确定这个过程是防错误的。此外,所有编译错误都给出了转换后代码的行号和文件名,而不是有效开发的代码。
我现在想写一个真实的编译器扩展,但是如果一些我没有想到的 C++ 规则与我想要的功能不兼容呢?这就是为什么我来到这里,解释这个想法,并询问是否有人认为它有什么问题。我还没有看到陷阱,但我不是一个Maven的标准,很明显,如果与C++的另一个特性直接不兼容,我就不想开始这种工作。
感谢您的阅读,这里是另一个使用的例子之前,我离开这个消息:

#include <string>
#include <vector>

// ===========================
// Class representing a person

class Person {

    public:

        // Getter and setter for name
        const std::string &getName() const {return _name;}
        setName(const std::string &name) {_name = name;}

        // Getter and setter for description
        const std::string &getDesc() const {return _desc;}
        setDesc(const std::string &desc) {_desc = desc;}

        // Getter and setter for annual wage
        int getWage() const {return _wage;}
        setWage(int wage) {_wage = wage;}

        // Setter for all things related to the identity
        setIdentity(const std::string &name, const std::string &desc) {setName(name).setDesc(desc);}

    private:

        std::string _name;
        std::string _desc;

        int _wage = 0;

};

// =============================
// Class representing a customer

class Customer : public Person {

    public:

        // Getter and setter for the order count
        int getOrd() const {return _ord;}
        setOrd(int ord) {_ord = ord;}

        // Getter and setter for the debt
        int getDebt() const {return _debt;}
        setDebt(int debt) {_debt = debt;}

        // Setter for all things related to business, the extension allows to set wage first
        setBusiness(int wage, int ord, int debt) {setWage(wage).setOrd(ord).setDebt(debt);}

        // A super secret function to run on the client data
        executeSecretCode() {/* Format data here to sell it to some Tech Giants later */}

    private:

        int _ord = 0;
        int _debt = 0;

};

// =============
// Main function

int main() {

    // Create the database of customers, the extension allows to call the setters in any order
    std::vector<Customer> db;
    db.push_back(Customer().setBusiness(91000, 2, 500).setIdentity("Alice", "The very first person ever").executeSecretCode());
    db.push_back(Customer().setWage(76000).setIdentity("Bob", "The very second person ever").executeSecretCode());
    db.push_back(Customer().setBusiness(55000, 1, 160).setName("Clara"));

}

sgtfey8w

sgtfey8w1#

C++23类实现了它作为一个可能的使用-演绎

struct Cat {
    template <class Self>
    auto&& chain_me(this Self&& self) {
        return std::forward<Self>(self);
    }
};

struct Displacer : Cat  {};

void main() {
   std::cout << typeid(Displacer().chain_me()).name() << std::endl;
}

字符串
这种形式显然对类型擦除不友好,它使源代码变干,代价是编译器得到一个WET端。

ubbxdtey

ubbxdtey2#

通过显式返回链接是手动编写代码以允许语法功能。
编译器比函数的实现者更了解调用方法的对象的类型和身份。强制imelmentor返回该特定对象以允许在调用位置使用特定技术看起来像是DRY违规。
这就是你在逃避的。
我很想使用this关键字,我们已经在“explicit this”的第一个参数中看到了。

struct nyat{
  this chain_me()const{}
};

字符串
它会返回这个
从这样的方法返回的值必须为void。
在调用站点,它允许链接。

nyat foo;
foo.chain_me().chain_me();


会被改写为

auto&& tmp=foo; tmp.chain_me(), tmp.chain_me();


唯一的陷阱是如果方法删除了this:但是如果你必须显式地标记这样的方法,它应该使问题变得明显。
这确实让我想强制使用return this;:使delete this; return this;更突出,我们可以忽略return this;。(OTOH,简洁有其自身的价值)。

相关问题