C++11 值初始化器与列表初始化器

C++11 value initializer vs list initializer

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

#include <initializer_list>

using namespace std;

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

struct bar {
  foo f{256};
};

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

更新

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

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

如果您根本无法更改 foo 的定义,那您就完蛋了。向拥有 class.

的人投诉

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

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是否移动。