c ++ 11中首选的初始化方法

时间:2014-07-26 15:06:16

标签: c++ c++11

int i = 0; // (a) Old C style should I use it?
int i{0}; // (b) Brace direct init
int i{}; // (c) Same as (b)
int i = {0}; // (d) as (b)
int i = {}; // (e) as (c)
auto i = 0; // (f) auto = int in this case.
auto i = int{0}; // (g) auto = more specific.
auto i = int{}; // (h) same as above (g)

使用哪一个? 萨特说使用:

int i = 0;
auto i = 0;

为什么不:

int i = {0};
auto i = int{0};

在某些情况下我应该摆脱“=”:

int i{0};
auto i{0}; // i is not what some might expect in this case. So I would prefer using "=" everywhere possible like int i = {0}; ...

修改 这就是我的目标,在我看来,这是最一致的:

rectangle       w   = { origin(), extents() }; 
complex<double> c   = { 2.71828, 3.14159 }; 
mystruct        m   = { 1, 2 }; 
int             a[] = { 1, 2, 3, 4 };
vector<int>     v   = { 1, 2, 3, 4 };
point           p   = {}; // Default initializes members
int             i   = {0}; // Checked assembly for this and it's binary the same as int i{0}; could be written also as int i = {};
string          s   = {""}; // Same as string s = {}; (OR) string s;

现实生活中的例子:

std::string       title              = { pt.get<std::string>("document.window.title") };
const std::string file               = { R"(CoreSettings.xml)" };
int_least64_t     currentTick        = { 0 }; // (OR) int_least64_t currentTick = {};
bool              isRunning          = { false }; // (OR) bool isRunning = {};
App*              app                = { nullptr }; // (OR) App* app = {};
Event             event              = {};
double            detectedFrameRate  = { 1000000000.0 / (swapIntervalDeltaCumulative / 20.0) };
double            precision          = { static_cast<double>(boost::chrono::high_resolution_clock::period::num)
                                           / boost::chrono::high_resolution_clock::period::den };
auto              timeSpan           = boost::chrono::duration_cast<boost::chrono::nanoseconds>(nowTime - startTime);

替代方案是:

std::string       title             { pt.get<std::string>("document.window.title") };
const std::string file              { R"(CoreSettings.xml)" };
int_least64_t     currentTick       { 0 }; // (OR) int_least64_t currentTick{};
bool              isRunning         { false }; // (OR) bool isRunning{};
App*              app               { nullptr }; // (OR) App* app{};
Event             event             {};
double            detectedFrameRate { 1000000000.0 / (swapIntervalDeltaCumulative / 20.0) };
double            precision         { static_cast<double>(boost::chrono::high_resolution_clock::period::num)
                                        / boost::chrono::high_resolution_clock::period::den };
auto              timeSpan          = boost::chrono::duration_cast<boost::chrono::nanoseconds>(nowTime - startTime);

如果不使用大括号,那就是丑陋或容易出错:

int_least64_t     currentTick        = 0; // C style - changed this from double to int recently and compiler did not complain so I had something like int_least64_t currentTick = 0.0; ugly!
bool              isRunning          = false; // C style
App*              app                = nullptr; // C mixed with C++11 style;
Event             event; // might not be initialized by all compilers
int               someInt            = func(); // func() returns double no error but narrowing.

7 个答案:

答案 0 :(得分:8)

对于简单的内容,例如示例中的int,我同意

int i=0; 

可能是最常被理解的(在程序员中),但使用大括号初始化有一些优点,对我而言,它更受欢迎。例如

int i = 3.99;    // i gets 3; no warning, no error
int i{3.99};     // i gets 3; warning: "narrowing conversion"

有助于编写更多无错误的代码,因此在我看来这是一种更好的方法。

将其与auto混合更为危险。我通常仅将auto用于:

  • 范围for for循环中的临时变量(例如for (const auto &n : mycollection)
  • 简化命名lambda的声明
  • 用于显式使用它们的迭代器实例(而不是range-for)
  • 模板化代码,这样做可以避免创建冗长的typedef

答案 1 :(得分:3)

有一些错误的派生词:

auto i{0}; // [comment omitted]
int i();

第一个将i定义为std::initializer_list<int> 第二个声明了一个名为i的extern函数返回int且没有参数。

规则经验法则:

  • 使用auto可以节省输入,并且类型或行为很明显。例子:

    auto x = new mymegathingy;
    auto y = container.begin();
    auto z = filestream.seekoff(0, basic_ios::curr);
    
  • 使用赋值可以工作(当lhs和rhs具有不同的类型时,当前编译器可以优化潜在的临时值。)

    int i = 0;
    int* i = 0; // For many types passing `nullptr` is better.
    
  • 使用通用初始化程序语法,其中赋值不起作用。

    std::vector<int> i = {1,2,3};
    auto i = new int[]{1,2,3};
    
  • 您可能希望使用直接构造函数调用,其中至少给出一个明显非类型的参数,以避免花括号:

    int i(0);
    

注意使用通用初始化程序语法进行初始化与auto混合不好,我们得到std::initializer_list<>

auto i{0};

当你没有传递至少一个明显的非类型参数时,避免使用旧式init,否则你可能会无意中声明一个函数:

int i();

答案 2 :(得分:2)

我不得不说我对一些人使用auto以及将来可能造成的潜在问题感到担忧。我认为auto在某些情况下是一个很好的工具,比如类型名称很长而且复杂(如迭代器)很难搞错。但对于int(其中int甚至比auto更短)这样的事情,我觉得我们可能会牺牲一些重要的类型安全性而无法获得明显的收益。

例如:

#include <iostream>

bool contains(const std::string& code)
{
    std::cout << __func__ << ": " << code << '\n';
    return true;
}

bool contains(int32_t value)
{
    std::cout << __func__ << ": " << value << '\n';
    return false;
}

struct user_t
{
    uint32_t id;
    std::string name;
};

int main()
{
    user_t user = {4, "bob"};

    // Imagine the original programmer creates somethin like
    // the following block:
    {
        auto i = user.name;

        if(contains(i))
            std::cout << "contained" << '\n';
    }

    // Then, sometime later, another programmer
    // does some bugfixing/maintenace and accidentally
    // uses the user's id instead of their username:
    {
        auto i = user.id; // now calls the wrong function and makes the wrong decision

        if(contains(i))
            std::cout << "contained" << '\n';
    }
}

使用auto使我们失去了一些我们期望从C ++中获得的静态类型检查。如果原始程序员使用了std::string iuint32_t i那么静态类型系统会在编译时保护我们免于产生一个微妙且难以找到的错误。

就我个人而言,我建议明智地使用自动判断循环中的短期变量。类型啰嗦和/或复杂的地方 - 人类难以推断。也是编译器必须推导它的地方(模板?)。

对于初始化,我会简单地说:

int i = 0; // simple, obvious, hard to improve upon. 

答案 3 :(得分:1)

对于int类型变量i = 0i = {0}是相同的。 int i = 0将是最易读的,因为这是人们习惯看到的。

如果您确实沿着自动路线走下去,则需要注意auto i{0}auto i = 0实际上定义了不同类型的事实。 (参见Deduplicator的评论)

请参阅此代码:

#include <iostream>
#include <typeinfo>

int main(){
    auto a = 0;
    std::cout << "a is of type:" << typeid(a).name() << std::endl;

    auto b = int{0};
    std::cout << "b is of type:" << typeid(b).name() << std::endl;

    auto c{0};
    std::cout << "c is of type:" << typeid(c).name() << std::endl;
}

当我们运行时,我们得到:

a is of type:i
b is of type:i
c is of type:St16initializer_listIiE

auto c{0}实际上是在创建一个std::initializer_list<int>,这几乎肯定不是发布问题的人所期望的。 在可读性方面,基本上有很多可能令人讨厌的东西。

这是我刚用g++ -Wall -std=c++11 main.cpp编译的内容(g ++版本4.7.2)

#include <iostream>
#include <typeinfo>
#include <vector>

class d{
    public:
        std::vector<int> v;
        d(std::initializer_list<int> l) : v(l) {
             std::cout << "constructed class d with a " << l.size() << "-element list\n";
        }
};

int main(){
    auto d{0};
    std::cout << "d is of type:" << typeid(d).name() << std::endl;
}

当我们运行时,我们得到:

d is of type:St16initializer_listIiE

这可能不是你所期望的。显然,如果你正在编写生产代码,你会想要选择更好的类名,但我很惊讶这在编译时没有给出警告。

答案 4 :(得分:0)

我同意Deduplicator。 我会用:

int i = 0;

这是最简单,最小,最着名的编码方式。

答案 5 :(得分:0)

我的推荐是:

1)仅对模板构造使用auto,实际上可以从中获益,对于类型名称可能很长的迭代器(如果您愿意)。不要将它用于原始类型,尤其不是引用。如果函数返回引用并且您想要使用auto,则需要使用auto&amp ;.如果不是副本,这可能非常棘手。

2)将代码视为可以在一张纸上阅读并明确的代码。如果修复了类型,请不要尝试隐藏类型。

3)初始化列表可能很危险。在需要时使用它们。我见过很多结构,使用初始化列表进行初始化,然后开发人员添加了另一个成员。对于某些编译器,不会抛出任何警告(无赖)。

答案 6 :(得分:-2)

完全使用auto name = initializer;。您需要auto&&auto name = (Base*)initializer;几个地方。绝对不要使用花括号,因为统一的初始化会被打破。