如何使用户定义的类型初始化*完全*像内置类型?

时间:2014-03-08 04:58:50

标签: c++ visual-c++ initialization value-initialization

我想创建一个包装数字类型的类型(并提供其他功能) 此外,我需要数字和包装器可以隐式转换

到目前为止,我有:

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 个答案:

答案 0 :(得分:3)

更新答案

好的,就像dyp指出的那样,我和其他人都错了。您可以使用默认构造函数= default实现您想要执行的操作:

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

这样做是因为如果没有初始化程序,您将获得我之前概述的相同行为,但是当您使用值初始化时,行为会按照 8 段落中所述进行更改:

  

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

原始答案

我认为没有办法按照你想要的方式完成这项工作。我们可以从草案标准部分8.5 初始化程序段落 12 中看到(类似于强调我的未来) EM>):

  

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

我们可以看到,除了段落 7 中的内置类型之外,这对类有不同的结果:

  

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

并包含以下项目符号:

  

- 如果T是(可能是cv限定的)类类型(第9节),则T的默认构造函数被称为(如果T没有可访问的默认构造函数,则初始化是错误的);

     

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

     

- 否则,不会执行初始化。

如果我们查看第二个案例Wrapper<unsigned int>()的第 11 段,它会说:

  

一个对象,其初始化程序是一组空的括号,即(),应进行值初始化。

然后回到段落 8

  

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

     

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

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

Praetorian aschepler 为您提供了稍微不同的选项,但似乎实现了您不希望使用相同语法的行为。

答案 1 :(得分:2)

我认为没有办法实现你想要的东西。一旦定义了一个类的默认构造函数,无论是在定义类的实例时提供还是省略括号,都会调用它。

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

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

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

Live demo

但是,我很快就会将隐式转换的要求降低到Wrapper,并将该类保持为聚合,而不是实现上述解决方案。

答案 2 :(得分:0)

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

答案 3 :(得分:0)

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

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

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

Wrapper<int> z {42};

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