I want to avoid all lvalue conversions from a type to another type:
struct A
{};
struct T
{
A a;
operator A() { return a; }
//operator A&() = delete; how to delete lvalue conversions to A?
};
void bar(A)
{}
void foo(const A&)
{}
void foo2(A&)
{}
int main()
{
T t;
bar(t); // fine
foo(t); // should never convert to ref-to-const A
foo2(t); // should never convert to ref-to A
return 0;
}
Is this possible?
How and which conversion operators do I need to delete?
Example on godbolt
You might do
struct T
{
A a;
operator A() { return a; }
template <typename T> operator const T&() = delete;
};
Demo
Related
I have some code which implies a type conversion, which does not compile although there is a conversion method...
class A
{
public:
A(void) :_m(0) { }
A(int val) : _m(val) {}
private:
int _m;
};
class B
{
public:
B(void) : _m(0) {}
B(int val) : _m(val) {}
B(const A&);
// there is a direct conversion operator here
operator A(void) const { return A(_m); }
operator int(void) const { return _m; }
private:
int _m;
};
int main()
{
B b;
A a = (A)b; // error C2440 here
}
Here is the error message:
error C2440: 'type cast': cannot convert from 'B' to 'A'
message : No constructor could take the source type, or constructor overload resolution was ambiguous
The error message means that these two operators
operator A(void) const { return A(_m); }
operator int(void) const { return _m; }
can be used in the expression
(A)b;
As a result using these conversion operators there can be used either the constructor A( int ) or the default copy constructor A( const A & ).
To make it more clear rewrite the corresponding declaration like
A a = A( b );
So whether the object b is converted to an object of the type A using the first conversion operator or to an object of the type int using the second conversion operator.
You could avoid the ambiguity declaring the operators for example like
operator A(void) const & { return A(_m); }
operator int(void) const && { return _m; }
that is for lvalues the first operator will be used and for rvalues the second operator will be used.
Here is your program with the modified operators.
#include <iostream>
class A
{
public:
A(void) :_m(0) { }
A(int val) : _m(val) {}
private:
int _m;
};
class B
{
public:
B(void) : _m(0) {}
B(int val) : _m(val) {}
B(const A&);
// there is a direct conversion operator here
operator A(void) const & { return A(_m); }
operator int(void) const && { return _m; }
private:
int _m;
};
int main()
{
B b;
A a = b;
A a1 = B();
}
From what I understand, the compiler tries several paths to interpret a = (A)b.
it finds the operator A
but it also finds the operator int on B, and the A(int) constructor which gives it a second path B => int => A...
And it does not know which to pick.
To fix the compilation, I can:
remove the operator int from B
rewrite the error line as A a = b.operator A();...
Is there some kind of functionality, where a pointer dereference gives an rvalue?
So, a usual pointer gives an lvalue:
Foo *p = ...;
fn(p[42]); // p[42] is an lvalue
I'd like to have p[42] return an rvalue, so a fn(Foo &&) can be called for fn(p[42]):
<some_pointer_type> p = ...;
fn(p[42]); // I'd like to have p[42] be an rvalue, so an fn(Foo &&) would be called
Note: I'd like to have p has this functionality, the pointer type should have this information, that dereferencing it should give an rvalue.
By means of std::move() you can perform an unconditional cast to a rvalue.
fn(std::move(p[42]));
The name move is actually misleading here: nothing is being moved, but a cast is being performed.
Consider then writing a pointer template Ptr whose operators * and [] are overloaded to return a rvalue:
#include <utility>
class Foo {};
template<typename T>
class Ptr {
public:
Ptr(T* ptr): ptr_{ptr} {}
T&& operator*() { return std::move(*ptr_); }
T&& operator[](int idx) { return std::move(*(ptr_ + idx)); }
private:
T* ptr_;
};
void fn(Foo&& x) { return; }
void fn(const Foo& x) { return; }
int main()
{
Foo foo[50];
Ptr<Foo> p{&foo[0]};
// call void fn(Foo&&)
fn(p[42]);
}
The type of result of pointer dereferencing, that is type result of some overloaded operator can not depend of some information stored at pointer object at runtime because its signature is defined at compile-time. However it possible to overload operator based on pointer origin:
#include <string>
#include <iostream>
#include <cstddef>
template<typename TItems>
class t_FancyPointer;
template<typename TItems>
class t_FancyPointer<TItems[]>
{
private: TItems * m_p_items;
public: explicit
t_FancyPointer(TItems * p_items) : m_p_items{p_items} {}
public: TItems &
operator [](::std::size_t const index) &
{
return(m_p_items[index]);
}
public: TItems &&
operator [](::std::size_t const index) &&
{
return(::std::move(m_p_items[index]));
}
};
void
fn(::std::string &&)
{
::std::cout << "rvalue reference" << ::std::endl;
}
void
fn(::std::string &)
{
::std::cout << "lvalue reference" << ::std::endl;
}
::std::string items[4];
t_FancyPointer<::std::string[]> perma_storage{items};
t_FancyPointer<::std::string[]> &
Get_PermaStorage(void)
{
return(perma_storage);
}
t_FancyPointer<::std::string[]>
Get_TempStorage(void)
{
return(t_FancyPointer<::std::string[]>{items});
}
int main()
{
// calls fn(::std::string &)
fn(Get_PermaStorage()[42]);
// calls fn(::std::string &&)
fn(Get_TempStorage()[42]);
return 0;
}
Output:
lvalue reference
rvalue reference
If you just want to always return && then you can define a single operator [] returning an rvalue reference:
public: TItems &&
operator [](::std::size_t const index)
{
return(::std::move(m_p_items[index]));
}
Though I have no idea why would you do something like this.
I am writing some code based on issue 28 smart pointer of more effective c++ as follows. However, it cannot compile:
main.cpp: In instantiation of 'SmartPointer<T>::operator SmartPointer<U>() [with U = MusicProduct; T = Cassette]':
main.cpp:99:17: required from here
main.cpp:48:39: error: invalid initialization of non-const reference of type 'SmartPointer<MusicProduct>&' from an rvalue of type 'SmartPointer<MusicProduct>'
return SmartPointer<U> (ptr_);
^
main.cpp:16:9: note: initializing argument 1 of 'SmartPointer<T>::SmartPointer(SmartPointer<T>&) [with T = MusicProduct]'
SmartPointer(SmartPointer<T>& other)
^
Either of these two changes works:
in the implementation of operator SmartPointer (), create an object and return:
SmartPointer a(ptr_);
return a;
Or, make the parameter of the copy constructor as const
SmartPointer(const SmartPointer& other)
and comment the line
other.ptr_ = nullptr;
Is there any reason why either of the solutions works? Thanks.
#include <iostream>
template <typename T>
class SmartPointer
{
public:
SmartPointer(T* ptr) : ptr_(ptr) {}
~SmartPointer()
{
if (ptr_)
{
delete ptr_;
}
}
SmartPointer(SmartPointer<T>& other)
{
ptr_ = other.ptr_;
other.ptr_ = nullptr;
}
SmartPointer<T>& operator = (SmartPointer<T>& other)
{
if (this == &other)
{
return *this;
}
if (ptr_)
{
delete ptr_;
}
ptr_ = other.ptr_;
other.ptr_ = nullptr;
return *this;
}
template <typename U>
operator SmartPointer<U> ()
{
// it works
//SmartPointer<U> a(ptr_);
//return a;
// error
return SmartPointer<U> (ptr_);
}
T& operator * () const
{
return *ptr_;
}
T* operator -> () const
{
return ptr_;
}
private:
T* ptr_ = nullptr;
};
class MusicProduct
{
public:
MusicProduct(const std::string& name) : name_(name) {}
virtual ~MusicProduct() {}
virtual void Play() const = 0;
virtual void ShowName() const
{
std::cout << name_ << std::endl;
}
private:
std::string name_;
};
class Cassette : public MusicProduct
{
public:
Cassette(const std::string& name) : MusicProduct(name) {}
void Play () const
{
std::cout << "play cassette" << std::endl;
}
};
void CallPlay(const SmartPointer<MusicProduct>& sp)
{
sp->Play();
}
int main()
{
SmartPointer<Cassette> a(new Cassette("Zhang"));
a->ShowName();
CallPlay(a);
return 0;
}
That's because your copy ctor has a non-const reference parameter and therefore cannot accept a temporary. Thus
return SmartPointer<X>(y);
won't work. The argument to the return keyword is a temporary, and it needs to be copied, so here the design breaks down.
Since you are using C++11, you can fix this by introducing a move constructor (and move assignment).
You can also make the argument const and designate the ptr_ member as mutable. This will allow you to copy from temporaries and const smart pointers, but for the price of actually mutating them.
I have the following (very simplified) "container" class:
class container
{
public:
template<typename T> container(const boost::shared_ptr<T> &rhs)
: m_content(rhs) { }
template<typename T>
operator T const & () const
{
return get<T>();
}
template<typename T>
T const & get() const
{
return *boost::any_cast< boost::shared_ptr<T> >(m_content);
}
private:
boost::any m_content;
};
It should store objects in the boost::any container in the form of a shared pointer. If I store some object, say, of the boost::shared_ptr<some_type> type in the container, I would like to get the reference (const some_type&) simply by a user-defined conversion which would allow to do something like this:
boost::shared_ptr<some_type> x(new some_type);
container cx = x;
...
// user-defined conversion
const some_type &y = cx;
// a template conversion using a "getter"
const some_type &y = cx.get<some_type>();
Sometimes, I need to store objects derived from some abstract type and do the same sort of type conversion to the reference of this abstract type, for example, like this:
boost::shared_ptr<some_abstract_type> x(new some_derived_type);
container cx = x;
...
// user-defined conversion
const some_abstract_type &y = cx;
// a template conversion using a "getter"
const some_abstract_type &y = cx.get<some_abstract_type>();
Both the user-defined conversion and the template "getter" work fine with GCC. However, the Intel C++ compiler seems to have a problem with the (user-defined) conversion while the "getter" works.
For example, the following code works with GCC but not with Intel:
#include <iostream>
#include <boost/any.hpp>
#include <boost/shared_ptr.hpp>
class container
{
public:
template<typename T> container(const boost::shared_ptr<T> &rhs)
: m_content(rhs) { }
template<typename T>
operator T const & () const
{
return get<T>();
}
template<typename T>
T const & get() const
{
return *boost::any_cast< boost::shared_ptr<T> >(m_content);
}
private:
boost::any m_content;
};
class base
{
public:
virtual ~base() { }
virtual void f() const = 0;
};
class derived : public base
{
public:
virtual ~derived() { }
virtual void f() const { std::cout << "hello\n"; }
};
void foo(const container &c)
{
const base & a = c;
a.f();
}
int main()
{
boost::shared_ptr<base> a(new derived);
container c = a;
foo(c);
}
With Intel, I get this error:
test.cpp(44): error: no suitable user-defined conversion from "const container" to "const base" exists
const base & a = c;
^
compilation aborted for test.cpp (code 2)
On the other hand, if I replace base with derived in both main() and foo() (or use the "getter" instead of the type conversion in foo()), everything works fine with Intel too. Is it possible to convince the Intel compiler to use the user-defined type conversion to the reference type when T is an abstract class?
Thanks in advance for any ideas.
EDIT: Interestingly, using the type conversion to the pointer type works fine. If I add
template<typename T>
operator T const * () const
{
return &get<T>();
}
to the container class and replace foo() with
void foo(const container &c)
{
const base * a = c;
a->f();
}
then it works also with Intel.
I would return a pointer in the getter:
template<typename T>
T const * get() const {
return boost::any_cast< boost::shared_ptr<T> >(m_content);
}
This avoids the conversion problem, and does not crash immediately if you pass a null pointer to your container.
Example:
void foo(const container &c)
{
const base* a = c.get<base>();
a->f();
}
You could also add a function valid() which checks if there is something in the container:
bool valid() const {
return m_content != NULL;
}
Edit: Your addition to your question follows exactly in this direction.
Ok, so it seems that it is a bug in the Intel C++ compiler and was filed in the bug tracking list.
I have a sequence of types, which I want to be freely convertible to one another. Consider the following toy example:
struct A {
int value;
A(int v) : value(v) { }
};
struct B {
int value;
B(int v) : value(v) { }
B(A a) : value(a.value) { }
operator A() const { return A(value); }
};
struct C {
int value;
C(int v) : value(v) { }
C(A a) : value(a.value) { }
C(B b) : value(b.value) { }
operator B() const { return B(value); }
operator A() const { return A(B(*this)); } // <-- ambiguous
};
int main(int argc, const char** argv) {
C c(5);
A a(3);
a = c;
}
So as you see, I'm trying to defined each subsequent type to be convertible from all previous types using cast constructors, and to be convertible to all previous types using cast operators. Alas, this does not work as intended, as the definition of C::operator A is ambiguous according to gcc 4.7:
In member function ‘C::operator A() const’:
19:40: error: call of overloaded ‘B(const C&)’ is ambiguous
19:40: note: candidates are:
9:3: note: B::B(A)
6:8: note: constexpr B::B(const B&)
6:8: note: constexpr B::B(B&&)
Changing the expression to static_cast<A>(static_cast<B>(*this)) doesn't change a thing. Removing that line altogether results in an error message in main, as no implicit conversion sequence may use more than one user-defined conversion. In my toy example, I could perform the conversion from C to A direcly, but in my real life application, doing so would cause a lot of duplicate code, so I'd really like a solution which reuses the other conversion operators.
So how can I obtain a set of three freely interconvertible types without duplicating conversion code?
I'd try this way in struct C:
operator A() const { return this->operator B(); }
Try this:
operator A() const { return A(B(value)); }
or this:
operator A() const { return A(operator B()); }