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

Tob*_*ann 20 c++ g++ optional clang++ c++17

给出以下代码:

#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;
}
Run Code Online (Sandbox Code Playgroud)

gcc 7.2.0写道 value: 1.

MSVC 2017(15.3)和clang 4.0.0然而写 value: 0.

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

Bar*_*rry 18

由于这是直接初始化,我们枚举构造函数并选择最佳构造函数.相关的构造函数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&
Run Code Online (Sandbox Code Playgroud)

两者都是可行的((8)如果只参与过载分辨率int是从constructible foo&foo既不是std::in_place_t也不std::optional<int>,所有这些容纳),但(8)是精确匹配,而(2)(3)需要用户定义的转换,所以它应该是优选的.gcc在这里错了.

但是,gcc实际上并没有调用它们(3).它只是直接my_opt从转换my_foo为a 的结果初始化optional<int>.这个程序用gcc 7.2版画3,但没有的 1a,1b或者2:

#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{});
}
Run Code Online (Sandbox Code Playgroud)

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

  • @ Rakete1111这里没什么不同.重载分辨率并非"总是"更喜欢非模板.给定两个具有等效转换序列排名的候选者,其中一个破坏者则优先选择非模板.但是我们这里没有等效的转换序列排名. (6认同)