使用初始化列表时,为什么非显式构造函数被显式隐藏?

时间:2013-02-16 16:51:22

标签: c++ c++11

考虑使用二叉树,其中节点有两个儿子或带有标签的叶子。此代码在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 / implicit / non-explicit /。很抱歉混淆,我使用“隐含”一词表示“允许隐式转换”,而使用“显式”作为“用关键字显式标记”。我调用编译器“default”自动生成的构造函数,所以我没有注意到“implicit”这个词会是如此模糊。

2 个答案:

答案 0 :(得分:4)

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

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

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

假设是这种情况,那么未标记为explicit的构造函数不会被标记为explicit 的构造函数隐藏,或不是你正在使用初始化列表。

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

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

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对象,并且没有尝试隐式转换。

答案 1 :(得分:0)

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

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之外的任何类型的转换以及作为第二个参数传递的变体。

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