【问题标题】:error: no suitable user-defined conversion from "Data" to "std::__cxx11::string" exists when using icc 17.0错误:使用 icc 17.0 时不存在从“Data”到“std::__cxx11::string”的合适的用户定义转换
【发布时间】:2021-05-03 14:44:24
【问题描述】:

我在尝试使用特定编译器版本进行编译时遇到编译器错误。 IE。 icc 17.0 与-std=c++17 -O3

编译器错误:

source>(19): error: no suitable user-defined conversion from "Data" to "std::__cxx11::string" exists
      Data temp{std::forward<Data>(d)};
                ^

compilation aborted for <source> (code 2)
ASM generation compiler returned: 2
<source>(19): error: no suitable user-defined conversion from "Data" to "std::__cxx11::string" exists
      Data temp{std::forward<Data>(d)};
                ^

compilation aborted for <source> (code 2)
Execution build compiler returned: 2

代码:


#include <string>
#include <vector>
#include <iostream>

struct Data {
  std::string id{};
  std::string rowData{};
  int totalRawDataLength{};

  std::vector<int> rawDataOffset{};
  std::vector<int> rawDataLength{};

  Data() = default;
  Data(const Data&d) =default;
  Data(Data &&d) =default;
};

Data ProcessData(Data &&d) {
    Data temp{std::forward<Data>(d)};
    // some code
    return temp;
}

int main() {
  Data d{};
  d.id = "id_001";
  d.rowData = "some data";
  d.rawDataOffset.emplace_back(4);
  d.rawDataLength.emplace_back(4);
  auto x = ProcessData(std::move(d));
  std::cout << "Test:" << x.id << std::endl;
  return 0;
}

Demo

以下代码适用于所有版本的gcc,并且它适用于具有相同编译器选项的更高版本的 icc。
它甚至适用于 icc 17.0 的 -std=c++11 -O3

在进一步调试中发现正在生成的默认复制构造函数有问题。

Code with copyconstructor

我无法理解发生了什么问题,听说这是某种编译器错误,在以后的版本中得到了解决?

【问题讨论】:

  • 如果将#include &lt;utility&gt; 添加到标题列表中会发生什么?
  • @dfrib nope 我没有尝试为不同 ABI 编译的链接库
  • @AdrianMole 得到同样的错误
  • 这似乎是在应用聚合初始化,而不是调用构造函数。您可以尝试Data temp(std::forward&lt;Data&gt;(d));(使用括号而不是大括号)和/或用Data() { } 替换默认构造函数来解决此问题。
  • @1201ProgramAlarm 您的建议解决了编译问题,即使用Data(const Data&amp;d) {} 替换默认复制构造函数也解决了编译问题。你能帮我理解发生了什么问题以及为什么在其他版本或不同的 c++ 标准中这不是问题

标签: c++ gcc c++17 icc


【解决方案1】:

这是the fickle aggregate 何时触发的又一个奇怪示例,此处与 ICC 编译器(版本 17.0)中的一个有争议的错误一起出现。


Data 是 C++14 和 C++17 中的聚合类(但不是 C++11)

在 C++11 中,聚合类被定义为,根据 [dcl.init.aggr]/1 (N3337) [emphasis mine]:

聚合是一个数组或一个类(子句 [class])没有用户提供的构造函数([class.ctor]),没有大括号或等式初始化器非静态数据成员([class.mem]),没有私有或受保护的非静态数据成员(子句 [class.access]),没有基类(子句 [class.access])派生]),并且没有虚函数([class.virtual])。

在C++14和C++17中,除了后者中的一些explicit细节外,规则与C++11中的大部分相同,只是要求

[...] 非静态数据成员没有大括号或等号初始化器

已被删除。

因此,由于您的类Data 没有用户提供的 构造函数(请参阅[dcl.fct.def.default]/4)而且只有公共数据成员,因此它是 C++14 和 C++ 中的聚合17,然而,由于它的默认成员初始化器,它不是 C++11 中的聚合。


ICC 错误地将聚合初始化应用于 C++14 和 C++17,其中 Data 是一个聚合

现在,接下来是直接支撑初始化

Data temp{std::forward<Data>(d)}

将枚举 C++11 中的构造函数重载,而在 C++14 和 C++17 中它是直接初始化,特别是通过以下规则(来自cppreference

T 类型对象的列表初始化的效果是:

  • 如果 T 是一个聚合类并且初始化列表具有相同或派生类型的单个元素(可能是 cv 限定的),则 对象从该元素初始化(通过复制初始化 复制列表初始化,或直接初始化 直接列表初始化)。

如果上述项目符号的条件不适用,聚合类的直接大括号初始化将是聚合初始化。这不应该在这里发生,但 ICC 似乎错误地在 C++14 和 C++17 中应用聚合初始化(其中 Data 是一个聚合类),使用 std::forward&lt;Data&gt;(d) 直接初始化Data 的第一个数据成员(即 id 类型的数据成员 std::string)而不是直接初始化 Data 本身;所有其他数据成员都由其关联的默认成员初始化器初始化。


用用户提供的构造函数替换显式默认的构造函数,使聚合类成为非聚合类

[...] 进一步调试发现正在生成的默认复制构造函数有问题。

在您在这里展示的示例中,您只是提供了您自己的复制构造函数,而不是在第一次声明时显式默认它。这意味着 Data 类不再是聚合(在 C++11 到 C++17 中的任何一个中)。


C++20 解决了大部分早期的聚合混淆

请注意,根据 C++20,大多数聚合混淆已经解决,根据 P1008R1 的实现(禁止使用用户声明的构造函数进行聚合),特别是不再允许聚合具有用户声明的构造函数,对于一个类作为聚合的要求比仅仅禁止用户提供的构造函数更严格。

【讨论】:

  • 说得通谢谢伙计!
猜你喜欢
  • 1970-01-01
  • 2016-01-28
  • 1970-01-01
  • 1970-01-01
  • 2014-11-04
相关资源
最近更新 更多