始终使用构造函数而不是显式转换运算符

时间:2017-07-16 15:16:40

标签: c++ templates type-conversion operator-overloading

我有以下课程:

template <typename T1>
class Foo {
public:
    Foo(T1* obj) : obj(obj) {}

    template <typename T2>
    Foo(const Foo<T2>& other) : obj(other.obj) {}

    template <typename T2>
    explicit operator Foo<T2>() {
        return Foo<T2>(static_cast<T2*>(obj));
    }
    T1* obj;
};

第二个构造函数的意图是,如果允许从Foo<X>Foo<Y>的隐式转换,则允许从X*Y*的隐式转换。

转换运算符允许使用从Foo<X>Foo<Y>的显式转换,从X*Y*进行显式转换。

但我注意到转换运算符永远不会被使用。即使我进行显式转换,编译器也总是使用第二个构造函数。如果无法隐式转换基础类型,则会导致错误。

以下代码可用于测试上述类。

class X {};
class Y : public X {};

int main() {
    Y obj;
    Foo<Y> y(&obj);
    Foo<X> x = y; // implicit cast works as expected.
    // y = x; // implicit conversion fails (as expected).
    // y = static_cast<Foo<Y>>(x); // conversion fails because constructor is
                                   // called instead of conversion operator.
}

有没有办法让编译器使用转换运算符进行显式转换?

2 个答案:

答案 0 :(得分:4)

对于static_cast<Foo<Y>>(x);,您尝试直接从Foo<Y>x)构建Foo<X>,对于此类上下文,转换构造函数是首选到conversion function

(强调我的)

  

如果可以使用转换函数和转换构造函数   执行一些用户定义的转换,转换功能和   构造函数都是通过重载解析来考虑的   复制初始化和引用初始化上下文,但仅限   构造函数在直接初始化上下文中被考虑

struct To {
    To() = default;
    To(const struct From&) {} // converting constructor
};

struct From {
    operator To() const {return To();} // conversion function
};

int main()
{
    From f;
    To t1(f); // direct-initialization: calls the constructor
// (note, if converting constructor is not available, implicit copy constructor
//  will be selected, and conversion function will be called to prepare its argument)
    To t2 = f; // copy-initialization: ambiguous
// (note, if conversion function is from a non-const type, e.g.
//  From::operator To();, it will be selected instead of the ctor in this case)
    To t3 = static_cast<To>(f); // direct-initialization: calls the constructor
    const To& r = f; // reference-initialization: ambiguous
}

您可以通过SFINAE使转换构造函数从此案例的重载集中丢弃;即只有在允许隐式转换底层指针时才使其有效。

template <typename T2, typename = std::enable_if_t<std::is_convertible<T2*, T1*>::value>>
Foo(const Foo<T2>& other) : obj(other.obj) {}

LIVE

答案 1 :(得分:2)

查看标准

中的以下引用

静态演员 [expr.static.cast / 4]

  

如果声明T格式正确,则可以使用static_cast格式static_cast<T>(e)将表达式e显式转换为T t(e);类型,对于一些发明的临时变量t(8.5)。这种显式转换的效果与执行声明和初始化,然后使用临时变量作为转换结果相同。当且仅当初始化将其用作左值时,表达式e才用作glvalue。

这基本上说构造函数比转换运算符更受欢迎,这就是发生的情况。