Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to define an implicit conversion operator to std::array?

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.

like image 240
kccqzy Avatar asked Mar 22 '23 15:03

kccqzy


1 Answers

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*
like image 146
jrok Avatar answered Apr 26 '23 22:04

jrok