从 ctor 的成员初始值设定项列表中的 std::initializer_list<CustomType> 初始化 std::list<std::string_view>

问题描述 投票:0回答:2
我有一个

Game

 类,将 
m_players
 存储为数据成员 (
std::list<Player>
),每个玩家都有多个数据成员,其中一个是他们的 
m_name
 (
std::string
)。实例化游戏时,我希望用户提供玩家的姓名。我希望他们能够使用字符串文字或字符串,所以我认为使用 
std::string_view
 是这里的最佳实践(对吗?)。我还希望游戏可以与任意数量的玩家一起玩,所以我认为游戏 ctor 参数的 
std::initializer_list
 是正确的选择(对吗?)。下面你可以找到我认为可以工作的代码(
godbolt link),但这给了我尝试初始化的代码行错误m_players

#include <iostream> #include <string> #include <list> struct Player { Player (std::string_view name) : m_name{name} {} private: std::string m_name; }; class Game { public: Game(const std::initializer_list<std::string_view> players) // error: no matching function for call to 'std::__cxx11::list<Player>::list(<brace-enclosed initializer list>)' : m_players{players} {} private: std::list<Player> m_players; }; int main() { Game game1{"Ann", "Bob"}; std::string name1{"Ann"}; std::string name2{"Bob"}; Game game2{name1, name2}; }
有两件事有效:

  1. 我可以编写以下循环,而不是使用成员初始值设定项列表: for (const auto& player : 玩家) { m_players.emplace_back(玩家); }

  2. 如果我使用固定数量的字符串视图作为输入参数而不是 std::initializer_list 也可以。

我不知道成员初始化列表中发生了什么,所以我不明白为什么这不起作用。我认为编译器基本上会完全执行 ctor 主体中的循环会执行的操作,但我的猜测是这是某种隐式转换问题?

如果我没有弄错的话,无论我使用成员初始值设定项列表还是带有循环的主体,都不应该对性能产生影响,但我仍然很好奇为什么这不起作用。

c++ c++17 string-view member-initialization stdinitializerlist
2个回答
0
投票

std::list<T>

std::initializer_list<T>
 构造,但 
Game
 采用 
std::initializer_list<std::string_view>
。就C++而言,模板类的两个不同模板实例化是完全独立的类型,彼此没有关系。这就是为什么即使 
std::initializer_list<std::string_view>
 可以从 
std::initializer_list<Player>
 隐式构造,也无法从 
Player
 转换为 
std::string_view

请注意,这有效:

std::list<Player> players{"Anna", "Bob"};
虽然这不是:

std::initializer_list<std::string_view> names = {"Anna", "Bob"}; std::list<Player> players{names};
因为在第一个示例中,

大括号封闭的初始化列表被转换为std::initializer_list<Player>

,这是
std::list
所需要的。但在第二个示例中,大括号括起来的初始值设定项列表被转换为 
std::initializer_list<std::string_view>
,然后无法将其转换回来。

按照 @super 的建议并编写

m_players{players.begin(), players.end()}

 调用 
std::list
 的构造函数,该构造函数需要两个迭代器。


0
投票
在查看了 cpp 参考之后,我想我终于明白了这个问题。 成员初始值设定项列表只是为每个数据成员调用相应的构造函数,在我的例子中是一个

std::list

 构造函数。
无论我选择什么作为输入类型(字符串或字符串文字),ctor 的输入参数 (
players
) 的类型都是 
const std::initializer_list<std::string_view>
 (T = std::string_view)。
m_players
 的类型为 
std::list<Player>
(T = 玩家),如果您检查 
std::list
 容器的文档,您会发现它提供了多个 ctor,其中一个接受 
std::initializier_list<T>
,其中 
T
 是与 
std::list<T>
 的类型相同。就我而言,它们是不同的(std::string_view!= Player),这就是问题所在。
解决这个问题的一种方法是使用 @super 提议的不同的 ctor。

: m_players{players.begin(), players.end()}
会打电话给这位演员:

template< class InputIt > list( InputIt first, InputIt last, const Allocator& alloc = Allocator() );
    
© www.soinside.com 2019 - 2024. All rights reserved.