结构体构造函数调用有歧义。

3

我在结构体“matrix”中有两个构造函数。

matrix(const unsigned int m, const unsigned int n);
matrix(const std::vector<std::vector<double>> &elements);

当我像这样调用它时
matrix mat({{1},{1}});

它抛出错误。
call of overloaded ‘matrix(<brace-enclosed initializer list>)’ is ambiguous
note: candidate: matrix::matrix(const std::vector<std::vector<double> >&)
note: candidate: matrix::matrix(const matrix&)

所以,它认为{{1},{1}}是一个"矩阵"对象,但是为什么呢?

@AnT,是的,因为我有向量的向量。我甚至可以输入{{1,2,3},{1,2},{1}},它也会被编译。 - Vlad Markushin
@AnT,是的,是我的错误,谢谢。 - Vlad Markushin
{1} 是一个 vector<double>{{1}, {1}}std::vector<std::vector<double>>。如果你移除第一个构造函数,代码仍然应该能够编译通过。 - greedy52
2个回答

5
所以它认为{{1},{1}}是'矩阵'对象,但是为什么?在你的示例代码中。
matrix mat({{1},{1}});

你明确告诉编译器尝试将该表达式与一个matrix构造函数匹配。

它不认为表达式{{1},{1}}是一个矩阵,而是因为你要求它这样做,所以它正在尝试将其转换为矩阵。

至于为什么会出现错误(虽然这不是你的问题,但似乎值得一提),那是因为

vector<double> v{1};

这是一个有效的向量声明,其值为1.0。

vector<vector<double>> vv{{1},{1}};

是一个有效的声明,其中包含两个元素向量,每个向量都有一个值为1.0的double元素。

mat{{{1},{1}}};

这个向量构造函数的参数是合法的。由于这种转换是隐式允许的,所以我们可以重写为:

mat m({{1},{1}});

as

mat m(mat{{{1},{1}}});

因此会存在歧义。请仔细注意圆括号和花括号。
您可以将构造函数设为隐式,或者习惯使用统一初始化风格,并写成以下形式:
mat m{{{1},{1}}};

首先。


@Useless:你应该提到 {{1}, {1}} 找到了第一个构造函数 - 这就是歧义的原因! - Vittorio Romeo
@VittorioRomeo 我也是这么想的。但出于某种原因,编译器提到了 matrix(const matrix&) 构造函数,而不是 matrix(const int, const int)。 - Vlad Markushin
1
{1,1} 无法匹配您的 vector<vector> 构造函数,只能匹配无符号构造函数。{{1}, {2}, {3}} 无法匹配无符号构造函数,只能匹配向量构造函数。我有点担心您在没有明确想法的情况下向该类抛出花括号的随机集合,也不知道它们匹配哪个构造函数或原因是什么。 - Useless
@Useless 哦,好的,我仔细地阅读了答案,现在我明白了。 - Vlad Markushin
我已经回过头来重新编写,以澄清所有括号和大括号所扮演的角色。 - Useless
显示剩余5条评论

3

当调用matrix mat({{1},{1}})时,编译器会找到这两个模棱两可的构造路径:

  • 通过在向量中构造两个元素的向量来调用vector<vector>>构造函数。

  • 隐式创建一个临时矩阵,然后使用该临时矩阵构造mat

    1. matrix(const unsigned int m, const unsigned int n)创建一个临时矩阵。(第一个{1}匹配到m,第二个{1}匹配到n。)

    2. 尝试使用matrix(const matrix&)从临时矩阵构造mat

将第一个(或两个)构造函数标记为explicit将明确使matrix mat({{1},{1}})调用...

matrix(const std::vector<std::vector<double>> &elements);

...one.

wandbox example


1
@VladMarkushin:从这里的输出结果http://melpon.org/wandbox/permlink/6qfAZ5Y7It0k5OJE可以清楚地看出来——基本上是的。 - Vittorio Romeo
1
@VladMarkushin:我更新了我的回答——现在清楚了吗? - Vittorio Romeo
将第一个构造函数标记为 explicit 是没有帮助的,因为你处于列表初始化的世界中,在那里 explicit 对于重载决议不起作用,只关乎是否允许调用 explicit 构造函数。 - Johannes Schaub - litb
1
有趣的是,只有GCC将此代码标记为非法。Clang接受。我认为GCC标记代码为非法是正确的。http://coliru.stacked-crooked.com/a/8ab8aa4bc6953454 - Johannes Schaub - litb
1
这个差异可以通过以下代码更容易地演示: http://coliru.stacked-crooked.com/a/ef65c26000452db3。这是标准的明确部分,clang似乎在这里违反了标准。 - Johannes Schaub - litb
显示剩余3条评论

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