为什么内联用户提供的构造函数odr使用基类构造函数? [英] Why does an inline user-provided constructor odr-uses base class constructors?

查看:84
本文介绍了为什么内联用户提供的构造函数odr使用基类构造函数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

考虑以下说明性示例

#include <iostream>

template <typename>
struct Base {
    static int const touch;
    Base() {
        (void)touch;
    }
};

template<typename CRTP>
int const Base<CRTP>::touch = []{
    std::cout << "Initialized!\n";
    return 0;
}();

struct A : Base<A> {
    A() {} 
};

struct B : Base<B> {
    B() = default;
};

int main() {
}

当上述程序由 GCC 编译时, VC ++ 并执行,一个人始终看到以下输出:

When the above program is compiled by GCC, Clang or VC++ and executed, one consistently sees the following output:

Initialized!

所有三个编译器均发出 Base< A> :: touch 的定义和初始化,而两个编译器均不发出 Base< B> :: touch 的定义和初始化(也通过Godbolt验证).因此,我可以得出结论,这是标准的认可行为.

All three compilers emit the definition and initialization of Base<A>::touch, whereas neither emits the definition and initialization of Base<B>::touch (verified also via godbolt). So I would conclude this is standard sanctioned behavior.

对于 B 的默认构造函数,我们有

For the defaulted constructor of B, we have

[class.ctor]

7 默认构造函数,而不是默认构造函数当它被用于创建其类类型的对象([intro.object])或在其第一次声明后被明确默认时,将隐式定义为delete.

7 A default constructor that is defaulted and not defined as deleted is implicitly defined when it is odr-used to create an object of its class type ([intro.object]) or when it is explicitly defaulted after its first declaration.

从中可以得出结论,由于在我们的TU中都不适用任何条件,因此永远不会隐式定义 B :: B().因此,它永远不会使用 Base< B> :: Base() Base< B> :: touch .我觉得这很合理.

From which one may conclude that since neither condition applies in our TU, B::B() is never implicitly defined. So it never odr-uses Base<B>::Base() and Base<B>::touch. I find this reasonable.

但是,我不明白为什么 A :: A()最终会使用其基类的成员进行odr.我们知道

However, I cannot understand why A::A() ends up odr-using the members of its base class. We know that

[class.mfct]

1 成员函数可以在其定义中类定义,在这种情况下,它是一个内联成员函数...

1 A member function may be defined in its class definition, in which case it is an inline member function ...

[dcl.inline]

6 应该定义一个内联函数或变量在每个使用该翻译单位的翻译单位中,并且在每种情况下都应具有完全相同的定义([basic.def.odr]).

6 An inline function or variable shall be defined in every translation unit in which it is odr-used and shall have exactly the same definition in every case ([basic.def.odr]).

[basic.def.odr]

4 ...的构造函数按照[dcl.init]中指定的方式使用一个类.

4 ... A constructor for a class is odr-used as specified in [dcl.init].

我们从不初始化任何类型为 A 的对象,因此我们不应该使用其构造函数进行odr.因此,我们的程序最终不会包含 A :: A()的任何定义.

We never initialize any objects of type A, so we shouldn't be odr-using its constructor. And so our program wouldn't end up containing any definition of A::A().

那么为什么它的行为就像定义一样存在?为什么它会使用 Base< A> :: Base()并导致其实例化?

So why does it behave as though a definition exists? Why does it odr-use Base<A>::Base() and causes its instantiation?

推荐答案

对于odr使用 Base< T> :: Base any 内联定义,会发生这种情况例如:

This happens for any inline definition that odr-uses Base<T>::Base, for example:

inline void x() {
    Base<char>();
}

struct A : Base<A> {
    A(int) {}
    void x() {
        Base<int>();
    }
};

struct C : Base<C> {
    C();
};

inline C::C() = default;  // See [1]

// Will print "Initialized!" four times

B()=默认不能使用 Base< T> :: Base ,因为它仅定义为 [basic.def]/2 ,它不是发射"的.由于[class.ctor]/7(如您所引用).仅当(并且)当对 B :: B()本身进行了odr使用时,才会(隐式)定义将使用 Base< T> :: Base 的定义.

B() = default does not odr-use Base<T>::Base because it is only defined as defaulted. Even though structurally a definition according to [basic.def]/2, it is not "emitted" because of [class.ctor]/7 (as you quoted). The definition that would odr-use Base<T>::Base will only be (implicitly) defined if and when B::B() is odr-used itself.

对于定义的其他内联函数,不存在这样的豁免.它们的定义无条件(且在结构上)包含odr-use Base< T> :: Base .在您的示例中, A :: A()是一个定义,而您的程序确实包含一个 A :: A()的定义,该定义使用 Base< T> :: Base .

For other inline functions that are defined, there does not exist such an exemption. Their definitions unconditionally (and structurally) contain an odr-use Base<T>::Base. A::A() in your example is a definition, and your program does contain a definition of A::A() that uses Base<T>::Base.

"应在使用了内联函数或变量的每个翻译单元中定义内联函数或变量"..这是必须"处理.要求(对于每个使用内联函数的TU,都需要一个定义),而不是使用内联函数的odr的结果.

"An inline function or variable shall be defined in every translation unit in which it is odr-used". This is a "shall" requirement (for every TU in which an inline function is odr-used, a definition is required), not the consequence of odr-using an inline function.

[1]作为 [dcl.fct.def.default]/5 :

在显式默认的位置定义了用户提供的显式默认函数(即在第一次声明后显式默认)

A user-provided explicitly-defaulted function (i.e., explicitly defaulted after its first declaration) is defined at the point where it is explicitly defaulted

因此, C :: C()具有默认定义,该默认定义会立即生成隐式"信息.定义.

So C::C() has a default definition which immediately generates the "implicit" definition.

这篇关于为什么内联用户提供的构造函数odr使用基类构造函数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆