为什么C++标准要求编译器忽略对基类型的转换运算符的调用?

OMG*_*chy 4 c++ type-conversion language-lawyer

请使用以下代码:

#include <iostream>

struct Base {
    char x = 'b';  
};

struct Derived : Base {
    operator Base() { return Base { 'a' }; }
};

int main() {
    Derived derived;
    auto base = static_cast<Base>(derived);

    std::cout << "BASE -> " << base.x << std::endl;
}
Run Code Online (Sandbox Code Playgroud)

在g ++和clang ++下,这会产生:

BASE -> b
Run Code Online (Sandbox Code Playgroud)

我期待以下内容:

BASE -> a
Run Code Online (Sandbox Code Playgroud)

为什么?因为为什么我读这段代码,我看到里面的转换运算符Derived返回一个Base包含的实例'a'.

clang ++让我礼貌地发出警告:

main.cpp:9:5: warning: conversion function converting 'Derived' to its base class 'Base' will never be used
    operator Base() { return Base { 'a' }; }
Run Code Online (Sandbox Code Playgroud)

研究这个警告,我发现这是设计的(为了清楚起见):

class.conv.fct

转换函数的类型([dcl.fct])是"不返回convert-type-id的参数的函数".转换函数永远不会用于将(可能是cv限定的)对象[...]转换为该类型(或对它的引用)的[可能是cv限定的]基类[...].

所以看起来两个编译器在这里做的都是正确的.我的问题是,为什么标准要求这种行为?

Nic*_*las 6

如果你可以在C++中覆盖转换为基类,那么你可以打破很多东西.例如,您究竟能够如何访问类的实际基类实例?您需要一些baseof模板,类似于std::addressof用于绕过il构想的operator&重载的模板.

允许这会产生关于代码含义的混淆.有了这个规则,很明显,在所有情况下,将类转换为其基类都会复制基类实例.


Bar*_*rry 5

让我们有一个Animals的层次结构,我想编写一个函数,它接受一个可修改的Animal,而不需要切片。我该怎么做?我有两个选择:

void by_ref(Animal& );
void by_ptr(Animal* );
Run Code Online (Sandbox Code Playgroud)

我可以称之为:

Dog dog = ...;
by_ref(dog);
by_ptr(&dog);
Run Code Online (Sandbox Code Playgroud)

今天,这两个调用之间的唯一区别将是两个函数内部使用的语法,并且可能对nullptr. 这是因为DogtoAnimal&Dog*toAnimal*是保证标准派生到基类的转换。没有替代。

但想象一下,如果我真的可以写:

struct Dog : Animal {
    operator Animal&() { ... };
};
Run Code Online (Sandbox Code Playgroud)

现在这两个调用可以做完全不同的事情!我Dog*Animal*转换仍然是同一条狗,但我DogAnimal&转换是完全不同的Dog. 它甚至可以是一个Cat. 这将使所有这些代码基本上无法推理。

您需要一种特殊的机制来明确地为您提供特定类型的基本子对象:

by_ref(std::base<Animal>(dog));
Run Code Online (Sandbox Code Playgroud)

它基本上必须在任何地方使用以保证任何依赖于继承的代码的正确性。这是很多代码。


有什么好处?如果你想要一个不同的基础子对象,你可以编写一个不同命名的函数:

struct Dog : Animal {
    Animal& foo();
};
Run Code Online (Sandbox Code Playgroud)

命名可能是关于编程两件难事之一,但最好只是想出你自己的名字而不是打开令人遗憾的袋子,这将允许人们编写他们自己的派生到基础但不是真的转换。