Allow implicit cast operator for const reference only - c++

Is there a way to make a custom cast operator only available (or only implicit) if the object is const?
Example:
class Foo;
class Bar;
class MyClass {
public:
operator Foo() const;
operator Foo() = delete; // this doesn't seem to have any effect
// I also tried explicit operator Foo(); - no change
operator Bar();
};
As you can see, I'd like MyClass to be implicitly cast to Foo if it is const, but to Bar if it is not const. This is because a pair of overloaded functions exists:
void f(Foo x);
void f(Bar x);
And I'd like to be able to pass MyClass to f, so that the correct function is selected depending on whether it is const or not. This way however, I am getting an ambiguous call to overloaded function. Why?
int main() {
f(MyClass());
}
Important: I know that I can make this work easily by turning the cast operators into constructors, but unfortunately, Foo, Bar, and f cannot be modified. For context, this is an idea to solve my other problem: Is there a way to resolve this ambiguous implicit cast operator overload?

The best viable overload is chosen before its access and/or removal is checked. As there is no best viable overload with the original class definition it doesn't even get to that stage. That is, the ambiguity needs to be resolved for the overload resolution already.
Making one of the two conversion operators explicit does resolve the issue (with the test program there are still errors due to Bar being incomplete). Using a combination of explicit (and = deleteed although that is optional) conversions does yield a version which may be what is looked for:
#include <iostream>
class Foo {};
class Bar {};
class MyClass {
public:
explicit operator Foo() const& = delete;
explicit operator Foo() && = delete;
operator Foo()& { return Foo(); }
explicit operator Bar() const& = delete;
operator Bar() && { return Bar(); }
explicit operator Bar() & = delete;;
};
void f(Foo) { std::cout << "f(Foo)\n"; }
void f(Bar) { std::cout << "f(Bar)\n"; }
int main() {
f(MyClass());
MyClass x;
f(x);
}
I didn't manage to create a version also accepting MyClass const y; f(y);: making the const& conversion operator non-explicit (for either conversion) causes an ambiguity elsewhere.

Related

Make method explicit except for friend classes

I am trying to create a conversion operator that would be explicit by default, except for some designated classes.
More precisely, I have a relatively simple class template whose instances should be convertible to some other type (int throughout this question, for simplicity). However, I want this conversion to be explicit by default, but still allow it to be implicit for one other class, namely the class passed as template argument. Without this last part, this is what it would look like:
template<typename T>
class A {
public:
A(int i) : i(i) {}
explicit operator int() const {
return i;
}
private:
int i;
};
Now, I want to be able to write something like A a(2); int i = a; inside the methods of T (which is supposed to be a class).
My first idea was to make use of friend declarations and declare a private overload of the conversion operator. However, overloading based solely on explicit isn't allowed. So I tried using const for that instead, and it worked:
template<typename T>
class A {
public:
A(int i) : i(i) {}
explicit operator int() { // Public non-const
return i;
}
private:
int i;
operator int() const { // Private const
return i;
}
friend T;
};
... until it doesn't. This works only when using non-const As, and while I don't plan on using const As, I still would like it to work in all cases. Note that if the public overload is const and the private one isn't, this works only using const As.
Here is a demo showing in which cases it works or doesn't.
I have thought of using volatile (demo), and while it makes it better, it still leads to the same problem: it only works when using non-volatile As.
Is there a better way to do that? More generally, is there a way to solve the problem in the very first sentence?
There is apparently no satisfying solution. The volatile option seems to be the most practical, and although it doesn't quite work with volatile As, an additional const_cast solves that problem.
template<typename T>
class A {
public:
A(int i) : i(i) {}
explicit operator int() const {
return i;
}
private:
int i;
operator int() const volatile {
return static_cast<int>(const_cast<A const&>(*this));
// or just
// return i;
}
friend T;
};
Demo
As Sneftel stated in the comments, access permissions tend to be granted on a pull basis (using) rather than a push basis. That might be why the language does not provide a way to change conversion semantics depending on the class or block performing the conversions.

Initialize const member variables

I have C++ code that boils down to something like the following:
class Foo{
bool bar;
bool baz;
Foo(const void*);
};
Foo::Foo(const void* ptr){
const struct my_struct* s = complex_method(ptr);
bar = calculate_bar(s);
baz = calculate_baz(s);
}
Semantically, the bar and baz member variables should be const, since they should not change after initialization. However, it seems that in order to make them so, I would need to initialize them in an initialization list rather than assign them. To be clear, I understand why I need to do this. The problem is, I can't seem to find any way to convert the code into an initialization list without doing one of the following undesirable things:
Call complex_method twice (would be bad for performance)
Add the pointer to the Foo class (would make the class size needlessly large)
Is there any way to make the variables const while avoiding these undesirable situations?
If you can afford a C++11 compiler, consider delegating constructors:
class Foo
{
// ...
bool const bar;
bool const baz;
Foo(void const*);
// ...
Foo(my_struct const* s); // Possibly private
};
Foo::Foo(void const* ptr)
: Foo{complex_method(ptr)}
{
}
// ...
Foo::Foo(my_struct const* s)
: bar{calculate_bar(s)}
, baz{calculate_baz(s)}
{
}
As a general advice, be careful declaring your data members as const, because this makes your class impossible to copy-assign and move-assign. If your class is supposed to be used with value semantics, those operations become desirable. If that's not the case, you can disregard this note.
One option is a C++11 delegating constructor, as discussed in other answers. The C++03-compatible method is to use a subobject:
class Foo{
struct subobject {
const bool bar;
const bool baz;
subobject(const struct my_struct* s)
: bar(calculate_bar(s))
, baz(calculate_baz(s))
{}
} subobject;
Foo(const void*);
};
Foo::Foo(const void* ptr)
: subobject(complex_method(ptr))
{}
You can make bar and baz const, or make the subobject const, or both.
If you make only subobject const, then you can calculate complex_method and assign to bar and baz within the constructor of subobject:
class Foo{
const struct subobject {
bool bar;
bool baz;
subobject(const void*);
} subobject;
Foo(const void*);
};
Foo::Foo(const void* ptr)
: subobject(ptr)
{}
Foo::subobject::subobject(const void* ptr){
const struct my_struct* s = complex_method(ptr);
bar = calculate_bar(s);
baz = calculate_baz(s);
}
The reason that you can't mutate const members within a constructor body is that a constructor body is treated just like any other member function body, for consistency. Note that you can move code from a constructor into a member function for refactoring, and the factored-out member function doesn't need any special treatment.
You may use delegate constructor in C++11:
class Foo{
public:
Foo(const void* ptr) : Foo(complex_method(ptr)) {}
private:
Foo(const my_struct* s) : bar(calculate_bar(s)), baz(calculate_baz(s)) {}
private:
const bool bar;
const bool baz;
};
If you don't want to use the newfangled delegating constructors (I still have to deal with compiler versions that don't know about them), and you don't want to change the layout of your class, you could opt for a solution that replaces the constructor with const void * argument by a static member function returning Foo, while having a private constructor that takes the output from complex_method as argument (that latter much like the delegating constructor examples). The static member function then does the necessary preliminary computation involving complex_method, and ends with return Foo(s);. This does require that the class have an accessible copy constructor, even though its call (in the return statement) can most probably be elided.

virtual tables and inheritance c++

i have a question regarding calling a function with virtual methods and multiple inheritence.
i have the following code:
#include<iostream>
using namespace std;
class A{};
class B:public A{};
class C:public B{};
class AA:public A{};
struct X
{
void f(A*) { std::cout<< "X::f(A*)\n";}
virtual void f(B*) { std::cout<< "X::f(B*)\n";}
void f(C*) { std::cout<< "X::f(C*)\n";}
virtual void f(C*) const { std::cout<< "const X::f(C*)\n";}
};
struct Y:public X {
virtual void f(B*) { std::cout<< "Y::f(B*)\n";}
void f(A*) { std::cout<< "Y::f(A*)\n";}
virtual void f(C*) const { std::cout<< "const Y::f(C*)\n";}
};
int main() {
Y* y=new Y();
y->f(new C);
}
I can't understand why this turns ambiguous and there are 2 candidates:
Y::f(B*)
Y::f(C*)
For an overloading function to be selected, it has to be the "best" at accepting each individual argument, including the implicit argument that becomes this. Best is defined in terms of the least conversions needed to convert the argument (in the caller) to the parameter (in the callee).
virtual void f(C*) const agrees perfectly with an argument of type C*, but the const qualifier at the end requires that this be converted from a non-const Y* to a Y const *. This is what the compiler is complaining about. If you cast
static_cast< Y const * >( y )->f(new C);
The problem goes away (although this isn't immediately illustrative since the extra qualification disqualifies the other overloads).
Note that all the overloads in X aren't even checked. The name resolution which finds all the overloads to be considered starts at the derived class and proceeds up the inheritance branches until it finds a matching name, and then it stops. To merge functions from multiple classes into one overload set, use a using declaration inside Y, using X::f;.
The actual solution to this problem is probably to introduce more matching overloads without const qualifiers at the end, so the const qualification of the calling pointer doesn't play such an unintuitive role.
Your overload for C is the only const member function.
y is non const and all overloads of f are acceptable, hence the call is ambiguous.
Ways to resolve the ambiguity:
Add a non const overload for C
Make y const
Make the overloads for A and B const
void f(C*) { std::cout << "X::f(C*)\n"; } in base class
is not visible due to name hiding.
use
using X::f;
in derived class, and it works as expected.

reusing the copy-and-swap idiom

I'm trying to put the copy-and-swap idiom into a reusable mixin:
template<typename Derived>
struct copy_and_swap
{
Derived& operator=(Derived copy)
{
Derived* derived = static_cast<Derived*>(this);
derived->swap(copy);
return *derived;
}
};
I intend it to be mixed in via CRTP:
struct Foo : copy_and_swap<Foo>
{
Foo()
{
std::cout << "default\n";
}
Foo(const Foo& other)
{
std::cout << "copy\n";
}
void swap(Foo& other)
{
std::cout << "swap\n";
}
};
However, a simple test shows that it is not working:
Foo x;
Foo y;
x = y;
This only prints "default" twice, neither "copy" nor "swap" is printed. What am I missing here?
This:
Derived& operator=(Derived copy)
doesn't declare a copy assignment operator for the base class (it has the wrong signature). So the default generated assignment operator in Foo will not use this operator.
Remember 12.8:
A user-declared copy assignment operator X::operator= is a non-static
non-template member function of class X with exactly one parameter of
type X, X&, const X&, volatile X& or const volatile X&.) [Note: an
overloaded assignment operator must be declared to have only one
parameter; see 13.5.3. ] [Note: more than one form of copy assignment
operator may be declared for a class. ] [Note: if a class X only has a
copy assignment operator with a parameter of type X&, an expression of
type const X cannot be assigned to an object of type X.
EDIT don't do this (can you see why ?):
You can do:
template<typename Derived>
struct copy_and_swap
{
void operator=(const copy_and_swap& copy)
{
Derived copy(static_cast<const Derived&>(copy));
copy.swap(static_cast<Derived&>(*this));
}
};
but you lose the potential copy elision optimization.
Indeed, this would assign twice the members of derived classes: once via copy_and_swap<Derived> assignment operator, and once via the derived class' generated assignment operator. To correct the situation, you'd have to do (and not forget to do):
struct Foo : copy_and_swap<Foo>
{
Foo& operator=(const Foo& x)
{
static_cast<copy_and_swap<Foo>&>(*this) = x;
return *this;
}
private:
// Some stateful members here
}
The moral of the story: don't write a CRTP class for the copy and swap idiom.
You cannot inherit assignment operators as a special case, if memory correctly serves. I believe that they can be explicitly using'd in if you need.
Also, be careful about over use of copy-and-swap. It produces non-ideal results where the original has resources that could be re-used to make the copy, such as containers. Safety is guaranteed but optimum performance is not.
I am afraid this is one area where a macro is necessary, because of the complex rules about automatically generated copy and assignment operators.
No matter what you do, you are in either of two cases:
You have provided (explicitly) a declaration of the assignment operator, in which case you are expected to provide a definition too
You have not provided (explicitly) a declaration of the assignment operator, in which case the compiler will generate one if the base classes and non-static members have one available.
The next question, therefore, is: Is it worth it to automate such writing ?
Copy-And-Swap is only used for very specific classes. I do not think it's worth it.
The compiler automatically generates a copy assignment operator for Foo, since there is none.
If you add a
using copy_and_swap<Foo>::operator=;
to Foo you will see an error telling you about the ambiguity on g++.
Maybe you could rewrite it so it looks like so:
template<class Derived>
struct CopySwap
{
Dervied &operator=(Derived const &other)
{
return AssignImpl(other);
}
Derived &operator=(Dervied &&other)
{
return AssignImpl(std::move(other));
}
private:
Derived &AssignImpl(Derived other)
{
auto self(static_cast<Derived*>(this));
self->swap(other);
return *self;
}
};
It'll probably all get inlined and likely won't be any slower than the original code.
This does not really answer the question (#Alexandre C. already did), but if you reverse the inheritance, you could make it work:
template<typename Base>
struct copy_and_swap : Base
{
copy_and_swap& operator=(copy_and_swap copy)
{
swap(copy);
return *this;
}
};
struct Foo_
{
Foo_()
{
std::cout << "default\n";
}
Foo_(const Foo_& other)
{
std::cout << "copy\n";
}
void swap(Foo_& other)
{
std::cout << "swap\n";
}
};
typedef copy_and_swap<Foo_> Foo;
int main()
{
Foo x;
Foo y;
x = y;
}

c++ class operator self-typecast

How can I create an operator function within a class that serves to typecast other types as an object of that class?
e.g.
class MyClass
{
// ...
// operator ??
// ...
}
int main()
{
MyClass obj;
int Somevar;
obj=(MyClass)Somevar; // class typecast
}
In general, is there an operator that allows this kind of typecast in exact syntax?
Just add a constructor that takes one argument:
class MyClass {
explicit MyClass(int x) { … }
};
called as:
MyClass x = static_cast<MyClass>(10); // or
MyClass y = MyClass(10); // or even
MyClass z(10);
This allows an explicit cast as in your example. (The C-style cast syntax is also supported but I won’t show it here because you should never use C-style casts. They are evil and unnecessary.)
Sometimes (but very rarely), an implicit cast is more appropriate (e.g. to convert from char* to std::string in assignments). In that case, remove the explicit qualifier in front of the constructor:
class MyClass {
MyClass(int x) { … }
};
Now an implicit conversion from int is possible:
MyClass a = 10;
However, this is usually not a good idea because implicit conversions are non-intuitive and error-prone so you should normally mark the constructor as explicit.
Define a constructor taking int argument.
But implicit conversions has some problems, so many that the language has the keyword explicit to prohibit them.
Mainly that's about overload resolution.
So, perhaps think twice before allowing the implicit conversion.
Cheers & hth.,
Provide non-explicit constructor with argument of wanted type:
class MyClass {
public:
MyClass( int x );
...
};
MyClass a = 42;
Note though: this is usually a bad idea.
You need to construct the object implicitly.
class MyClass
{
int x;
public:
MyClass(int X = 0):x(X){} //also serves a default constructor
}
int main()
{
MyClass obj = Somevar; // implicit type construction
}
why not use operator=() ?
class MyClass
{
public:
Myclass& operator=()(int i) {
//do what you want
return *this;
}
}
int main()
{
MyClass obj;
int Somevar;
obj = Somevar; // call operator=(somevar)
}