【问题标题】:C++11 value initializer vs list initializerC++11 值初始化器与列表初始化器
【发布时间】:2017-06-28 01:42:26
【问题描述】:

从经验上看,C++ 总是更喜欢列表初始化器而不是值初始化器。因此,我的问题是如何强制对也支持直接列表初始化的类型进行值初始化。这是一个最小的非工作示例:

#include <initializer_list>

using namespace std;

struct foo {
  foo(int) {}
  foo(initializer_list<char>) {}
};

struct bar {
  foo f{256};
};

在此示例中,我希望使用构造函数 foo(int) 而不是 foo(initializer_list&lt;char&gt;) 来初始化 f。但是,GCC 和 Clang 都拒绝该代码,因为 256 对于字符来说太大了,这意味着 C++ 规范需要选择列表初始值设定项。显然注释掉foo 的第二个构造函数可以解决问题。定义bar 以便在字段f 上使用值初始化的最简单方法是什么?理想情况下,我可以避免复制初始化f,因为在我的真实示例中没有复制构造函数。

更新

澄清一下:当然可以在bar 的每个构造函数中显式初始化f。但我的问题是关于成员初始化语法的,因为在有大量构造函数的情况下,最好只在一个地方初始化某些字段,而不是全部复制代码。

【问题讨论】:

  • 写一个构造函数?
  • foo f(256);.
  • 我想看看为什么initializer_list&lt;char&gt;{256} 的可行构造函数。此外,根据编译器行为推断有关规范的事情也是不安全的。有时多个编译器会出错。
  • @M.M: "我想看看为什么initializer_list&lt;char&gt;{256} 的可行构造函数" 因为标准总是更喜欢initializer_list在可能的情况下使用构造函数,并且 256 可以隐式转换为 char。因此是可能的。但是,它是非法的,因为将 256 转换为 char 是一种窄化转换,这在花括号初始化列表中是不允许的。
  • @BaummitAugen: foo f(256) 不起作用,因为这是非法的成员声明语法。例如,g++ 说,“错误:数字常量之前的预期标识符”。

标签: c++ c++11 initializer-list


【解决方案1】:

只要您要求foo 是不可复制/可移动的,并且您要求foo 有一个可以用来代替构造函数的initializer_list 构造函数,这就是您得到的行为。因此,如果你想解决这个问题,你必须改变这些事实之一。

如果您根本无法更改foo 的定义,那么您就完蛋了。向拥有该课程的人投诉。

第二个事实可能是最容易改变的,但即使这样也不会没有后果:

struct il {};

struct foo {
  foo(int) {}
  foo(il, initializer_list<char>) {}
};

这完全消除了问题的歧义。 foo{256} 将始终调用单整数构造函数。但是,foo 在技术上没有 initializer_list 构造函数;相反,您必须使用标签类型 il 来调用它并使用值的初始化列表:

foo f{il{}, {/*actual list*/}};

这需要更多的支撑,但没有真正的选择。

请注意,在 C++17 中,保证省略允许您这样做:

struct bar {
  foo f = foo(256);
};

不管foo 是否移动。

【讨论】:

  • 有趣的是,foo f = foo(256) 在 C++17 中有效,即使 foo 的复制构造函数被显式删除。谢谢。
猜你喜欢
  • 1970-01-01
  • 2020-02-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多