在转换为std :: optional <t>时,Clang和GCC的结果不同

时间:2017-08-23 15:09:16

标签: c++ g++ optional c++17 clang++

给出以下代码:

#include <iostream>
#include <optional>

struct foo
{
    explicit operator std::optional<int>() {
        return std::optional<int>( 1 );
    }
    explicit operator int() {
        return 0;
    }
};

int main()
{
    foo my_foo;
    std::optional<int> my_opt( my_foo );
    std::cout << "value: " << my_opt.value() << std::endl;
}

gcc 7.2.0 writes value: 1

MSVC 2017(15.3)和clang 4.0.0 however write value: 0

根据C ++标准,哪一个是正确的?

1 个答案:

答案 0 :(得分:18)

由于这是直接初始化,我们enumerate the constructors并选择最好的一个。 std::optional的相关构造函数是:

constexpr optional( const optional& other ); // (2)
constexpr optional( optional&& other ) noexcept(/* see below */); // (3)

template < class U = value_type >
/* EXPLICIT */ constexpr optional( U&& value ); // (8), with U = foo&

两者都可行(如果(8)可以从int构建而且foo&既不是foo也不是std::in_place_tstd::optional<int>只参与重载解析,所有这些都保持),但(8)是完全匹配,而(2)(3)需要用户定义的转换,因此应该首选。 gcc在这里错了。

但是,gcc实际上也没有调用(3)。它只是直接从my_opt转换为my_foo的结果初始化optional<int>。此程序使用gcc 7.2打印3但不打1a1b2

#include <iostream>

template <class T>
struct opt {
    opt() { }
    opt(opt const& ) { std::cout << "1a\n"; }
    opt(opt&& ) { std::cout << "1b\n"; }

    template <class U>
    opt(U&& ) { std::cout << "2\n"; }
};

struct foo 
{
    explicit operator opt<int>() { std::cout << "3\n"; return {}; }
};

int main()
{
    opt<int> o(foo{});
}

我不认为这是一条允许的路线。我提交了81952