7

我想制作一种包装数字类型(并提供附加功能)的类型。
此外,我需要数字和包装器都可以相互隐式转换

到目前为止,我有:

template<class T>
struct Wrapper
{
    T value;
    Wrapper() { }
    Wrapper(T const &value) : value(value) { }
    // ... operators defined here ...
};

几乎很好,但它的行为与内置类型不太一样:

#include <iostream>

int main()
{
    unsigned int x1, x2 = unsigned int();
    Wrapper<unsigned int> y1, y2 = Wrapper<unsigned int>();

    std::cerr << x1       << std::endl;  // uninitialized, as expected
    std::cerr << y1.value << std::endl;  // uninitialized, as expected

    std::cerr << x2       << std::endl;  // zero-initialized, as expected
    std::cerr << y2.value << std::endl;  // uninitialized!?!
}

我有什么办法可以设计Wrapper这样的陈述

Wrapper<unsigned int> y2 = Wrapper<unsigned int>();

初始化value内部,强制声明,如

Wrapper<unsigned int> y1;

也这样做?

换句话说,是否有可能创建一个在初始化方面与内置类型完全相同的类型?

4

4 回答 4

3

更新的答案

好的,正如 dyp 指出的那样,我和其他人都错了。= default您可以使用默认构造函数来实现您想要做的事情:

 Wrapper() = default ;
           ^^^^^^^^^

这是有效的,因为如果没有初始化器,您将获得与我之前概述的相同的行为,但是当您使用值初始化时,行为会发生第8段中所述的变化:

— 如果 T 是(可能是 cv 限定的)非联合类类型,没有用户提供或删除的默认构造函数,则该对象为零初始化,如果 T 具有非平凡的默认构造函数,则默认初始化;

原始答案

我不认为有办法让这项工作按照你想要的方式进行。类类型的行为与内置类型不同,我们可以从标准草案的 8.5 Initializers12段中看到这一点(强调我的未来):

如果没有为对象指定初始化器,则该对象是默认初始化的;如果不执行初始化,则具有自动或动态存储持续时间的对象具有不确定的值。[ 注意:具有静态或线程存储持续时间的对象是零初始化的,请参见 3.6.2。——尾注]

我们可以看到这对于类的结果与第7段中的内置类型不同,它说:

默认初始化 T 类型的对象意味着:

并包括以下项目符号:

— 如果 T 是(可能是 cv 限定的)类类型(第 9 条),则调用 T 的默认构造函数(如果 T 没有可访问的默认构造函数,则初始化是非良构的);

— 如果 T 是数组类型,则每个元素都是默认初始化的;

否则,不执行初始化。

如果我们查看第二种情况的第11Wrapper<unsigned int>()段,它说:

初始值设定项为空括号集的对象,即 (),应进行值初始化。

然后回到第8段:

对 T 类型的对象进行值初始化意味着:

— 如果 T 是(可能是 cv 限定的)类类型(第 9 条),没有默认构造函数(12.1)或用户提供或删除的默认构造函数,则该对象是默认初始化的;[...]

所以我们最终会得到相同的行为。

Praetorian和aschepler为您提供了工作方式略有不同的选项,但似乎可以实现您想要的行为,只是语法不同。

于 2014-03-08T05:28:05.210 回答
2

我认为没有任何方法可以实现您正在寻找的东西。只要您为将调用的类定义了默认构造函数,无论您在定义类的实例时是否提供或省略括号。

您可以通过声明以下构造函数来接近;变量定义将需要一对空的大括号来实现值初始化。

Wrapper(std::initializer_list<std::initializer_list<T>> /*unused*/) : value() {}

auto y3 = Wrapper<unsigned int>({}); // y3.value will be value initialized

现场演示

但我宁愿放弃隐式转换的要求Wrapper,并将类保持为聚合,而不是实现上面的解决方案。

于 2014-03-08T05:38:58.403 回答
0

不幸的是,不是我能想到的。C++ 隐式转换class_type name为调用默认构造函数。您必须使默认构造函数执行您期望未初始化的原始类型执行的操作。

于 2014-03-08T05:24:37.527 回答
0

如果删除用户提供的构造函数,则可以在默认构造时保持成员未初始化,或者对包装器进行值初始化,并在这样做时将其存储(以及其成员)初始化为零:

unsigned int x1, x2 {}; // One uninitialized, one value-initialized
Wrapper<unsigned int> y1, y2 {}; // Ditto

您仍然可以在构建期间通过聚合初始化设置值:

Wrapper<int> z {42};

无论如何,这在很大程度上是不必要的。除了引入微妙的、难以重现的错误外,未初始化的值很少有用。我建议在默认构造函数或成员声明中对成员进行值初始化。

于 2014-03-08T08:40:51.110 回答