【问题标题】:Why are non-explicit constructors hidden by explicit ones when using initializer lists?为什么在使用初始化列表时非显式构造函数被显式构造函数隐藏?
【发布时间】:2013-02-01 11:46:08
【问题描述】:

考虑以下二叉树,其中节点有两个儿子或带有标签的叶子。此代码在 g++ 4.7.2 中编译:

#include <memory>
using namespace std;

struct Tree {
    unique_ptr<Tree> left, right;
    char label;

    Tree(char label) : label(label) {}

    Tree(Tree && left, Tree && right) :
        left(new Tree(move(left))), 
        right(new Tree(move(right))) {}

    //~ explicit Tree(Tree && left, bool right) {}

} tree {{1, 2}, 3};

当我取消注释显式构造函数时,它无法编译(如果这个显式构造函数是私有的,它也会失败)。这是 gcc 错误,还是显式构造函数禁止对具有相同数量参数的非显式构造函数使用初始化列表构造?

编辑:s/隐式/非显式/。抱歉,我使用“隐式”一词表示“允许隐式转换”,而“显式”表示“用关键字显式标记”。我将编译器自动生成的构造函数称为“默认”,所以我没有注意到“隐式”这个词会如此含糊。

【问题讨论】:

  • 哪个“显式构造函数”?我看到两个。而且AFAIR,在您定义构造函数的那一刻,编译器没有定义默认构造函数。
  • 无论使用{}还是()初始化语法都应该编译失败。

标签: c++ c++11


【解决方案1】:

你的问题似乎假设了一些不正确的东西,并揭示了一个根本的误解。

首先,隐式生成的构造函数在“使用初始化列表时”不会隐藏。无论您是否将其标记为explicit,只要您定义构造函数,它们的生成就会被简单地禁止。

但请注意:“隐式”构造函数通常是指当您不提供时由编译器隐式(自动)生成的构造函数,而不是缺少explicit 修饰符的构造函数。在上面的段落中我说的是前者,但我被引导相信你正在考虑后者。

假设是这种情况,没有标记为explicit的构造函数被标记为explicit的构造函数隐藏是不正确的一般使用初始化列表。

您的程序无法编译的原因是您的explicit 构造函数恰好比非explicit 更好,因为它接受bool 作为它的第二个参数,它只需要标准转换匹配参数2。另一方面,接受Tree&amp;&amp; 的非explicit 构造函数需要用户定义的转换,包括临时Tree 对象的构造,并且标准转换优于用户定义的转换。

要强制编译器从参数2 构造一个Tree 临时而不是将2 转换为bool(因此,调用非explicit 构造函数),您必须指定这个通过将构造函数调用更改为以下内容:

struct Tree
{
    ...
} tree {{1, Tree(2)}, 3}; // Explicitly specify the second argument is to be
                          // used to create a temporary Tree object, rather
                          // than being converted to a bool

还要注意,虽然这同样适用于顶层树的创建(即3 也被转换为bool,并且您的explicit 构造函数被再次调用),但这不是问题在这种情况下,因为您正在直接初始化 tree 对象,并且没有尝试隐式转换。

【讨论】:

  • 您关于转换偏好的论点似乎是合理的。在这种情况下考虑显式构造函数只是导致编译器错误是标准定义的行为吗?
  • @krdln:不确定你的意思,但如果你问为什么编译器不追溯重载决议并在发现最佳匹配导致麻烦时选择另一个构造函数,那么是的,这是标准行为。选择了最佳匹配,如果结果调用不合法(例如,因为构造函数是 private,或在本例中为 explicit),则会发出错误。
【解决方案2】:

您的问题的解决方案可能是:

template<
  typename Bool,
  typename=typename std::enable_if<
    std::is_same<
      typename std::decay<Bool>::type,
      bool
    >
  >::type
>
explicit Tree(Tree && left, Bool&& right) {}

我已经阻止了除bool 之外的任何类型的转换,以及作为第二个参数传递的变体。

但是,您最好创建一个静态方法来执行此构造函数想要做的事情,而不是在问题上抛出更多重载的构造函数。

【讨论】:

    猜你喜欢
    • 2017-05-02
    • 1970-01-01
    • 2017-06-13
    • 1970-01-01
    • 1970-01-01
    • 2018-07-22
    • 1970-01-01
    • 2013-06-07
    • 2013-07-29
    相关资源
    最近更新 更多