如何禁用隐式构造函数转换,同时允许复制初始化。

6
假设我们有以下代码:
class U {
  ...
}

并且:

class T {
  T(const U&) { ... }
}

现在我可以这样声明一个变量:

U foo;

然后使用以下方式:

T blah(foo); 或者 T blah = foo;

个人而言,我更喜欢后一种方式。

现在问题来了,我应该修改T的拷贝构造函数吗?

class T {
  explicit T(const U&) { ... }
}

我只能这样声明变量: T blah(foo); 使用T blah = foo;会导致编译错误,因为无法将U转换为T。 http://en.cppreference.com/w/cpp/language/explicit解释了这种行为: “指定构造函数和(自C++11以来的)转换运算符,不允许隐式转换或复制初始化。”
现在,我工作的人要求我们所有的构造函数都是显式的。 作为一个老家伙,我不喜欢改变我的编码风格,并忘记T blah = ...风格。
问题如下: “有没有一种方法可以使构造函数显式,同时允许复制初始化语法?”
有很好的理由使构造函数显式,大多数情况下,您确实希望使其显式。
在这些情况下,我认为可以采取以下措施:
class T {
  template<typename = V>
  T(const V&) = delete;
  T(const U&) { ... }
}

请问有没有一种可以使用的技巧,以便创建一个 catch-all 构造函数,禁止所有转换,除了我实际需要的那个。

谢谢。

编辑:根据 Matt McNabb 的答案指出的错字进行了更正。感谢。

1个回答

3
T blah = U();因为你正确地解释了,复制初始化需要将U隐式转换为T,而你标记了构造函数为explicit,所以出现了错误。(注意:这不是复制构造函数)
显式转换应该像这样:T blah = T(U());,这样就可以没有错误。 T blah(U());是一个函数声明(查找most vexing parse)。在你的测试用例中,你可能没有真正尝试将blah用作对象,否则你会注意到这个问题。

回答你的问题:

有没有一种方法可以使构造函数显式,同时允许拷贝初始化语法?

根据你引用的关于explicit的确切文字所解释的那样,是没有这种方式的:

指定构造函数[...],不允许[...]拷贝初始化。

您将不得不切换到直接或花括号初始化。我认为这是一件好事,因为复制初始化很麻烦,只适用于避免MVP;但现在我们可以使用花括号初始化避免MVP,因此它已经不再必要。

您可以使用以下任何一种,因为在所有情况下,T都是通过列表元素直接和显式地初始化的:

T blah{ U() };
T blah = T{ U() };  // redundant copy/move operation, probably elided

请注意,这里不能使用T blah = { U() };的形式进行初始化,因为这种初始化方式(称为复制列表初始化)不能使用显式构造函数。

1
T blah = { U() };并不意味着T blah = { T(U()) };。复制列表初始化甚至在概念上也不会构造临时的T - T.C.
1
@T.C. 好的,你能帮我编辑一下最后一段,让它正确无误(如果需要,还可以修正其他术语)。我检查了编译器而不是标准,发现它拒绝了 T blah = { U(); },因为构造函数是显式的,所以我认为这是因为必须创建一个 T - M.M
你说得对...我写的太快了,试图概括问题。我的实际用法应该是:U foo; T blah = foo; - jyavenard
@MattMcNabb 好的,已编辑。标准规定在这种情况下禁止直接使用显式构造函数(即,如果重载解析选择了一个,则代码是非法的)。 - T.C.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接