When the two return arguments to the conditional operator c?x:y
are not of the same type, a copy is made before the cast is applied. Can this be prevented while still keeping simple useabiltiy?
I have this (trimmed down for questions):
struct Fixed {
char data[10];
Fixed(char *s) { strcpy(data, s); }
operator char*() { return this->data; }
};
But it has really bad behavior with the conditional operator and nullptr:
Fixed f =...; // just here to show the type of f, don't read too much into this
...
bool condition = ...;
char *s = condition ? nullptr : f;
A copy of f is made and s is now pointing to a value on the stack that will go away soon. This all happens because type of nullptr
is std::nullptr_t
. f
will go through the cast to char*
, but only after it is copied first. This seems like extremely poor behavior, but it is what the spec says.
My current fix is to just make the cast and ctor explicit
, but that kind of ruins to usability a little. Are there any other solutions?
Here's some example code to play with (ignore the quality as I was heavily playing with this to see how gcc and LLVM handle this differently):
#include <cstdio>
#include <iostream>
#include <array>
#include <cstring>
using namespace std;
struct A : public array<char,4> {
A() { cerr<<"def\n"; }
A(const A &o) { cerr<<"copy\n"; (*this)=o;}
A(const char *s) { cerr<<"ctor\n";assign(s); } // explicit fixes
void assign(const char*s) {cerr<<"assign\n";memset(this->begin(), 0, 4); strncpy(this->begin(), s, 4); }
operator char*() { cerr<<"cast\n";return this->begin(); }
//operator void*() { cerr<<"void\n";return this->begin(); }
//operator std::nullptr_t() { cerr<<"void\n";return (std::nullptr_t)this->begin(); }
};
volatile A *faraway = new A();
char* plain(A &v) { cerr<<"in pl\n";
return faraway == nullptr ? nullptr : v;
}
char* cast1(A &v) { cerr<<"in c1\n";
return faraway == nullptr ? (char*)nullptr : v;
}
char* cast2(A &v) { cerr<<"in c2\n";
return faraway == nullptr ? nullptr : (char*)v;
}
int main() {
A *a = new A; a->assign("asd");
char *x = a->data();
cerr << "\nplain\n";
char *yp = plain(*a);
cerr << "\nc1\n";
char *y1 = cast1(*a);
cerr << "\nc2\n";
char *y2 = cast2(*a);
cerr << "\n---\n";
cerr << (void*)a << "\n" << (void*)(a->data()) << "\n" << (void*)x << "\n---\n";
cerr << (void*)yp << "\n" << (void*)y1 << "\n" << (void*)y2 << "\n";
return 0;
}
Your problem is that the operands of the ternary have types std::nullptr_t
and struct Fixed
. The inference rule looks for conversions from one operand type to the other, as well as common base classes. There is no opportunity to infer char*
.
You can catch the mistake automatically, by providing operator std::nullptr_t() = delete
;