gpt4 book ai didi

c++ - C++14 和 C++17 之间的默认构造函数调用区别

转载 作者:搜寻专家 更新时间:2023-10-30 23:51:45 26 4
gpt4 key购买 nike

考虑以下代码(问题如下):

#include <iostream>

struct Type0
{
Type0(char* c)
{}
};

struct Type1
{
Type1(int* i=nullptr) : i_(i)
{}

Type1(const Type1& other) = default;

int* i_;
};

template <typename ...>
struct Composable;

template <typename T0, typename ... T>
struct Composable<T0, T...> : T0, Composable<T...>
{
Composable()
{
std::cout << "Default Invoked: " << sizeof...(T) << std::endl;
}

Composable(const Composable& other) = default;

template<typename Arg, typename ... Args>
Composable(Arg&& arg, Args&& ... args) :
T0(std::forward<Arg>(arg)), Composable<T...>(std::forward<Args>(args)...)
{
std::cout << "Non-default invoked: " << sizeof...(T) << std::endl;
}
};

template <>
struct Composable<>{};

int main()
{
int i=1;
char c='c';

auto comp = Composable<Type0, Type1>(&c, &i);

std::cout << comp.i_ << std::endl;
}

您可以找到实时代码here .这段代码有一个有趣的特性:取决于您是使用 --std=C++17 还是 --std=C++14 选项编译它,行为更改(您可以在我的实时代码链接中尝试此操作:编辑左下角的 g++ 调用 --std 参数)。

使用 --std=c++14,您将获得以下输出:

Non-default invoked: 0
Non-default invoked: 1
Default Invoked: 0
Non-default invoked: 1
0x0

使用--std=C++17,你会得到这个:

Non-default invoked: 0
Non-default invoked: 1
0x7ffcdf02766c

对我来说,这种差异令人费解。很明显,C++17 版本做的是正确的,而 C++14 是错误的。 C++14 版本正在调用 Composable 和(从中)Type1 的默认构造函数(这是 0x0 最后一行输出来自,因为 Type1 提供此作为其 i 构造函数参数的默认值)。但是,我没有看到应该调用默认构造函数的任何地方。

此外,如果我完全注释掉 Composable 的默认构造函数,C++17 版本会做与以前完全相同的事情,而 C++14 版本现在无法编译,提示关于缺少默认构造函数。如果有任何希望通过不同的优化行为以某种方式解释差异,那么这个事实肯定会扼杀它(无论如何希望很小,因为观察到的差异在所有优化级别(包括 0)中都存在)。

谁能解释一下这个区别? C++14 的行为是错误,还是我不理解的某些预期行为?如果 C++14 的行为在 C++14 的规则内是正确的,有人可以解释默认构造函数调用的来源吗?

最佳答案

保证复制省略。

这一行:

auto comp = Composable<Type0, Type1>(&c, &i);

在 C++17 中,这与以下内容完全相同:

Composable<Type0, Type1> comp(&c, &i);

如果您更改到此版本,您将看到 C++14 和 C++17 之间的相同行为。然而,在 C++14 中,这仍然是一个移动构造(或者,正如您稍后将看到的更技术上正确的,复制初始化)。但是在Composable ,您没有隐式生成的移动构造函数,因为您有一个用户声明的复制构造函数。因此,对于移动构造,您的“非默认调用”构造函数模板在 C++14 版本中被调用(它比复制构造函数更匹配):

template<typename Arg, typename ... Args>
Composable(Arg&& arg, Args&& ... args) :
T0(std::forward<Arg>(arg)), Composable<T...>(std::forward<Args>(args)...)

在这里,ArgComposable<Type0, Type1>Args是一个空包。我们委托(delegate)给T0 ( Type0 ) 的构造函数,转发整个 Composable (这是有效的,因为它公开地继承自 Type0,所以我们在那里得到隐式生成的移动构造函数)和 Composable<Type1>的默认构造函数(因为 args 是空的)。

这个构造函数模板真的不是一个合适的移动构造函数——它最终不会初始化 Type1完全是成员(member)。而不是从右侧的 Type1::i_ 移动你正在调用 Type1::Type1() ,默认构造函数,这就是为什么你最终得到 0 的原因.

如果你添加一个合适的移动构造函数:

Composable(Composable&& other) = default;

然后您会再次看到 C++14 和 C++17 之间的相同行为。

关于c++ - C++14 和 C++17 之间的默认构造函数调用区别,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53618592/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com