3

I have the following code snippet:

struct T {
    T(const T&) = default;
    T(const S &);
};

struct S {
    operator T();
};

int main() {
    S s;
    T t = s; // copy-initialization of class type
    return 0;
}

My question is why the compiler prefers S::operator T() for the initialization of t rather than reporting an error that the initialization is ambigious. In my opinion the following happens (correct me if i am wrong):

  • t is copy-initialized with an lvalue of type S
  • S is not T and S is also not a subclass of T, so S and T are unrelated
  • because of the fact that the variable t is copy-initialized and the fact that the types S and T are unrelated, the compiler tries to find user-defined-conversion sequences to do the initialization.
  • overload resolution is responsible for selecting the best user-defined-conversion which can be either a converting constructor of T or the conversion function of S
  • the implicite conversion sequence for the constructor T::T(const S&) from the argument s is the identity conversion because the lvalue s can be bound directly to this lvalue reference
  • the implicite conversion sequence for the conversion function S::operator T() from the argument s is also the identity conversion, because the implicit object parameter is S&

Both the constructor and the conversion function return a prvalue of type T which can be used to direct-initialize the variable t. That means that the second standard conversion sequence of both user-defined-conversion sequences is the identity conversion.

This would mean that both user-defined-conversion sequences are equally good. Or is there a special rule which prefers the conversion functions?

I was reading the following rules in the c++11 standard:

The initialization that occurs in the form T x = a; as well as in argument passing, function return, throwing an exception (15.1), handling an exception (15.3), and aggregate member initialization (8.5.1) is called copy-initialization.

The semantics of initializers are as follows...If the destination type is a (possibly cv-qualified) class type: If the initialization is direct-initialization, or if it is copy-initialization where the cv-unqualified version of the source type is the same class as, or a derived class of, the class of the destination, constructors are considered.... Otherwise (i.e., for the remaining copy-initialization cases), user-defined conversion sequences that can convert from the source type to the destination type or (when a conversion function is used) to a derived class thereof are enumerated as described in 13.3.1.4, and the best one is chosen through overload resolution (13.3)

User-defined conversion sequence U1 is a better conversion sequence than another user defined conversion sequence U2 if they contain the same user-defined conversion function or constructor and if the second standard conversion sequence of U1 is better than the second standard conversion sequence of U2

Maybe i am making false assumptions. I hope you can help me!

4

2 回答 2

2

S使用转换运算符进行的转换比T使用S constas 参数的转换要好。如果您创建s一个S const,则首选构造函数:您的身份操作在一种情况下确实是身份操作,在另一种情况下则不是。如果您对成员进行转换运算符Sconst则会产生歧义。下面是一个演示所有情况的测试程序:

struct S;
struct T {
    T(const T&) = default;
    T(const S &);
};

struct S {
    S(); // needed to allow creation of a const object
#ifdef AMBIGUOUS
    operator T() const;
#else
    operator T();
#endif
};

int main() {
#ifdef CONST
    S const s;
#else
    S s;
#endif
    T t = s; // copy-initialization of class type
    return 0;
}
于 2013-09-04T23:08:46.607 回答
0

我想我找到了澄清这一点的规则:

13.3.3.2: ... S1 和 S2 是引用绑定 (8.5.3),并且引用所引用的类型是相同的类型,除了顶级 cv-qualifiers 和引用由 S2 初始化的类型比由 S1 初始化的引用所引用的类型更具有 cv 限定。

在成员函数 S::operator T() 中,隐式对象参数的类型 S& 直接绑定到类型 S 的左值 s。在构造函数 T::T(const S&) 中,参数直接绑定到左值 s类型为 S,但此引用绑定比运算符函数更符合 cv 要求,因此重载决议首选运算符函数。

你同意吗?

于 2013-09-04T23:25:13.500 回答