从备选方案返回的变体中返回可构造变量

时间:2019-03-06 10:30:21

标签: c++

具有可以从变体构造的结构(例如,从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_tvariant的转换,但看起来只是伪造的诊断。综上所述,我认为我可以提出一个简单的原因,说明上述构造不起作用:它需要两次隐式转换,但是规则是只考虑一次。

1 个答案:

答案 0 :(得分:0)

下面的语句起作用是因为正在执行list initialization

return {type1_t{ }};
  

副本列表初始化

return { arg1, arg2, ... } ;    (8) 
     

在以下情况下执行列表初始化:
  ...
  直接列表初始化(考虑显式和非显式构造函数)
  ...
  8)在带有braced-init-list作为返回表达式的return语句中,并且 list-initialization初始化返回的对象

此处var_holder_ttype1_t对象初始化,并且按预期方式工作。