具有可以从变体构造的结构(例如,从std :: variant构造),并返回从变体的替代之一隐式构造的结构,可以吗?
考虑以下代码:
#include <utility>
struct type1_t
{
};
struct type2_t
{
};
struct variant
{
/*template <typename T>
variant(T&& t)
{}*/
variant(type1_t){}
};
struct var_holder_t
{
var_holder_t(variant v)
: m_var(std::move(v))
{}
private:
variant m_var;
};
var_holder_t foo()
{
return type1_t{ }; // <====== offending line
}
MSVC 2017、2019允许这样做,但是GCC和clang无法编译。更改为其他变体的构造函数,甚至使用boost :: variant或std :: variant都无济于事。
值得一提的是,将有问题的行更改为return {type1_t{ }};
会使它以某种方式进行编译。目前我迷路了,尽管从技术上来说,再添加两个{}不会对可读性造成太大的损害,但也许我会坚持这一点,只是希望得到一个答案。
也使var_holder_t的构造函数模板(转发)有所帮助,它使var_holder_t可从type1_t直接构造(并且很可能在性能方面要好得多),但此刻我想让var_holder_t完全非模板-这应该是(想法,设计)为随便编写的简单代码。
更新:真正让我困惑的是Clang发出此消息:
note: candidate constructor not viable: no known conversion from 'type1_t' to 'variant' for 1st argument var_holder_t(variant v)
这很奇怪,因为显然有一个从type1_t
到variant
的转换,但看起来只是伪造的诊断。综上所述,我认为我可以提出一个简单的原因,说明上述构造不起作用:它需要两次隐式转换,但是规则是只考虑一次。
答案 0 :(得分:0)
下面的语句起作用是因为正在执行list initialization。
return {type1_t{ }};
副本列表初始化
return { arg1, arg2, ... } ; (8)
在以下情况下执行列表初始化:
...
直接列表初始化(考虑显式和非显式构造函数)
...
8)在带有braced-init-list作为返回表达式的return语句中,并且 list-initialization初始化返回的对象
此处var_holder_t
用type1_t
对象初始化,并且按预期方式工作。