I am trying to have a C++ class that can be implicitly converted to std::array
. Conversion works, but it is not implicit.
#include <array>
class A {
private:
std::array<float, 7> data;
public:
operator std::array<float, 7>&() { return data; }
operator const std::array<float, 7>&() const { return data; }
};
int main() {
A a;
a[1] = 0.5f; // fails to compile
auto it = a.begin(); // fails to compile
A b;
static_cast<std::array<float, 7>>(b)[1] = 0.5f; //ok
auto it2 = static_cast<std::array<float, 7>>(b).begin(); //ok
return 0;
}
I understand the above example is quite convoluted, as it basically completely exposes a private
member of the class. But this is an oversimplified example, I am just trying to tackle the problem of why implicit conversions to std::array
does not work.
I have tried the above example with both clang-3.2
and gcc-4.8
. Neither compiles.
Even more perplexing is that if I use implicit conversion to pointer, compilation apparently succeeds:
operator float *() { return data.begin(); }
operator const float *() const { return data.cbegin(); }
But of course, this means losing the many niceties of std::array
, which I will accept if there isn't a better solution.
I'm answering your question from a comment:
Could you please elaborate on why my conversion does not make sense? While trying to resolve operator[], why should the compiler not consider possible conversions?
Short answer, because that's how it works. A conversion operator to a built-in type can be called here, not to user-defined type.
A bit longer answer:
When an operator is used in an expression, overload resolution follows the rules laid out in 13.3.1.2
.
First:
2 If either operand has a type that is a class or an enumeration, a user-defined operator function might be declared that implements this operator or a user-defined conversion can be necessary to convert the operand to a type that is appropriate for a built-in operator. In this case, overload resolution is used to determine which operator function or built-in operator is to be invoked to implement the operator [...].
a[1]
is, for this purpose interpreted as a.operator[](1)
, as shown in Table 11
in the same section.
The lookup is then performed as follows:
3 For a unary operator @ with an operand of a type whose cv-unqualified version is T1, and for a binary operator @ with a left operand of a type whose cv-unqualified version is T1 and a right operand of a type whose cv-unqualified version is T2, three sets of candidate functions, designated member candidates, non- member candidates and built-in candidates, are constructed as follows:
— If T1 is a complete class type, the set of member candidates is the result of the qualified lookup of T1::operator@ (13.3.1.1.1); otherwise, the set of member candidates is empty. [1]
— The set of non-member candidates is the result of the unqualified lookup of operator@ in the context of the expression according to the usual rules for name lookup in unqualified function calls (3.4.2) except that all member functions are ignored. However, if no operand has a class type, only those non-member functions in the lookup set that have a first parameter of type T1 or “reference to (possibly cv-qualified) T1”, when T1 is an enumeration type, or (if there is a right operand) a second parameter of type T2 or “reference to (possibly cv-qualified) T2”, when T2 is an enumeration type, are candidate functions. [2]
— For the operator ,, the unary operator &, or the operator ->, the built-in candidates set is empty. For all other operators, the built-in candidates include all of the candidate operator functions defined in 13.6 that, compared to the given operator,
— have the same operator name, and
— accept the same number of operands, and
— accept operand types to which the given operand or operands can be converted according to 13.3.3.1, and [3]
— do not have the same parameter-type-list as any non-template non-member candidate.
The result is as follows:
[1]
finds nothing (there's no operator[]
in your class[2]
finds nothing (there's no free function operator[]
and neither of operands are enumeration types)[3]
finds built-in operator[](float*, std::ptrdiff_t)
because A
declares a conversion to float*
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With