User-defined conversion operator doesn't work on references - c++

I have a simple primitive type wrapper:
template <typename T>
class Scalar {
public:
explicit Scalar(T value) : value{value} {}
Scalar(Scalar&& other) = default;
Scalar& operator=(Scalar&& other) = default;
Scalar(const Scalar& other) = default;
Scalar& operator=(const Scalar& other) = default;
template <typename U>
explicit operator Scalar<U>() {
return Scalar<U>{static_cast<U>(this->value)};
}
inline T getValue() const noexcept { return this->value; }
private:
T value;
};
Casting Scalar values works well, but somehow it fails for references, e.g.
auto a = Scalar<double>{2.54};
Scalar<int> b = static_cast<Scalar<int>>(a); // works
const auto& c = a;
Scalar<int> d = static_cast<Scalar<int>>(c); // fails
Here's the compilation error (can be checked here http://rextester.com/GOPYU13091), any ideas what might be the issue here?
source_file.cpp: In function ‘int main()’:
source_file.cpp:31:47: error: no matching function for call to ‘Scalar(const Scalar<double>&)’
Scalar<int> d = static_cast<Scalar<int>>(c);
^
source_file.cpp:12:3: note: candidate: Scalar<T>::Scalar(const Scalar<T>&) [with T = int] <near match>
Scalar(const Scalar& other) = default;
^
source_file.cpp:12:3: note: conversion of argument 1 would be ill-formed:
source_file.cpp:31:47: error: could not convert ‘c’ from ‘const Scalar<double>’ to ‘const Scalar<int>&’
Scalar<int> d = static_cast<Scalar<int>>(c);
^
source_file.cpp:10:3: note: candidate: Scalar<T>::Scalar(Scalar<T>&&) [with T = int] <near match>
Scalar(Scalar&& other) = default;
^
source_file.cpp:10:3: note: conversion of argument 1 would be ill-formed:
source_file.cpp:31:47: error: could not convert ‘c’ from ‘const Scalar<double>’ to ‘Scalar<int>&&’
Scalar<int> d = static_cast<Scalar<int>>(c);
^

This is a const vs non-const issue, not a reference vs object issue.
Using
auto& c = a;
Scalar<int> d = static_cast<Scalar<int>>(c);
works for me.
By changing the user defined conversion operator to const member function
template <typename U>
explicit operator Scalar<U>() const {
return Scalar<U>{static_cast<U>(this->value)};
}
also makes sure that the following works.
const auto& c = a;
Scalar<int> d = static_cast<Scalar<int>>(c);

Related

Inaccessible operator == breaks child class with spaceship operator

I bumped in GCC error, which can be demonstrated with the following simplified program. Base struct A has an operator == with some argument type other than A. Child struct B defines default spaceship operator <=>, which shall implicitly define operator == as well. But since no appropriate equality operator is found in the base class, B::operator == must be implicitly deleted. Instead, the resulting struct B appears broken in a way that no object of it can be created:
#include <compare>
struct A {
std::strong_ordering operator <=>(const A &) const = default;
bool operator==(int) const;
};
struct B : A {
virtual std::strong_ordering operator <=>(const B &) const noexcept = default;
};
B b; //GCC error here
Demo: https://gcc.godbolt.org/z/584nhfxxs
Error message:
error: use of deleted function 'virtual constexpr bool B::operator==(const B&) const'
9 | virtual std::strong_ordering operator <=>(const B &) const noexcept = default;
| ^~~~~~~~
<source>:9:34: note: 'virtual constexpr bool B::operator==(const B&) const' is implicitly deleted because the default definition would be ill-formed:
<source>:8:8: error: no match for 'operator==' (operand types are 'A' and 'A')
8 | struct B : A {
| ^
<source>:5:10: note: candidate: 'bool A::operator==(int) const' (reversed)
5 | bool operator==(int) const;
| ^~~~~~~~
<source>:5:21: note: no known conversion for argument 1 from 'A' to 'int'
5 | bool operator==(int) const;
Is it simply a bug in GCC?

C++ SFINAE with interface

I can't understand how i can create expr object from double in expr.cpp file.
expr_base:
The base class of all expressions. Note that all expression classes
(including this base) are private to the implementation and should not
be exposed to other code. The rest of the program should use
expressions only via expr.
This subclasses std::enable_shared_from_this to enable getting
shared_ptr to this from a method.
expr:
Wrapper around dynamically allocated instances of expr_base. This type
has value semantics and since all subclasses of expr_base are
immutable, shallow copies are made.
This type has overloaded functions and operators, so that expression
construction is easy and readable.
error:
error: no viable conversion from returned value of type 'typename enable_if<!is_array<number>::value,
shared_ptr<number> >::type' (aka 'std::__1::shared_ptr<exprs::number>') to function return type 'expr'
return std::make_shared<exprs::number>(n);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/..../expr.hpp(...): note: candidate constructor (the implicit copy constructor) not viable: no known conversion from 'typename enable_if<!is_array<number>::value, shared_ptr<number> >::type' (aka 'std::__1::shared_ptr<exprs::number>') to 'const expr &' for 1st argument
class expr final {
^
/.../expr.hpp:(...): note: candidate constructor (the implicit move constructor) not viable: no known conversion from 'typename enable_if<!is_array<number>::value, shared_ptr<number> >::type' (aka 'std::__1::shared_ptr<exprs::number>') to 'expr &&' for 1st argument
/.../expr.hpp:(...): note: candidate template ignored: requirement 'std::is_convertible<exprs::number *, const expr_base*>::value' was not satisfied [with T = exprs::number]
expr(std::shared_ptr<T> e): ptr(std::static_pointer_cast<const expr_base>(std::move(e))) {}
expr.hpp
...
class expr;
class expr_base: public std::enable_shared_from_this<expr_base>
{
friend class expr;
protected:
expr_base() = default;
public:
using variable_map_t = std::map<std::string, double>;
virtual ~expr_base() = default;
};
class expr final {
private:
using const_pointer = std::shared_ptr<const expr_base>;
public:
using variable_map_t = expr_base::variable_map_t;
template <typename T, typename = std::enable_if_t<std::is_convertible<T*, const expr_base*>::value>>
expr(std::shared_ptr<T> e): ptr(std::static_pointer_cast<const expr_base>(std::move(e))) {}
expr() = default;
static expr number(double n);
operator const_pointer const &() const {return ptr;}
const expr_base* operator->() const {assert(ptr.get() != nullptr); return ptr.get();}
private:
const_pointer ptr;
};
expr.cpp
...
#include "expr.hpp"
#include "expr_impl.hpp"
expr expr::number(double n) {
return std::make_shared<exprs::number>(n); // It doesn't work
}
expr_impl.hpp
...
#include "expr.hpp"
namespace exprs {
class number:expr_base {
private:
double num_;
public:
number(double num): num_(num) {};
};
}

Problem trying to call copy constructor in base class which has a std::any constructor

Consider the following code:
#include <any>
struct A {
A();
A(const A&) = default;
explicit A(std::any value);
};
struct B: A {
B() : A() { }
B(const B& b) : A(b) {}
explicit B(std::any value) : A(value) {}
};
The compiler reports the following error on compilation (using clang++-5 or higher):
In file included from <source>:2:
In file included from /opt/compiler-explorer/gcc-7.3.0/lib/gcc/x86_64-linux-gnu/7.3.0/../../../../include/c++/7.3.0/any:37:
In file included from /opt/compiler-explorer/gcc-7.3.0/lib/gcc/x86_64-linux-gnu/7.3.0/../../../../include/c++/7.3.0/new:40:
In file included from /opt/compiler-explorer/gcc-7.3.0/lib/gcc/x86_64-linux-gnu/7.3.0/../../../../include/c++/7.3.0/exception:143:
In file included from /opt/compiler-explorer/gcc-7.3.0/lib/gcc/x86_64-linux-gnu/7.3.0/../../../../include/c++/7.3.0/bits/nested_exception.h:40:
In file included from /opt/compiler-explorer/gcc-7.3.0/lib/gcc/x86_64-linux-gnu/7.3.0/../../../../include/c++/7.3.0/bits/move.h:54:
/opt/compiler-explorer/gcc-7.3.0/lib/gcc/x86_64-linux-gnu/7.3.0/../../../../include/c++/7.3.0/type_traits:149:31: error: no member named 'value' in 'std::is_copy_constructible<B>'
: public conditional<_B1::value, __and_<_B2, _B3, _Bn...>, _B1>::type
~~~~~^
/opt/compiler-explorer/gcc-7.3.0/lib/gcc/x86_64-linux-gnu/7.3.0/../../../../include/c++/7.3.0/any:192:27: note: in instantiation of template class 'std::__and_<std::is_copy_constructible<B>, std::__not_<std::is_constructible<B, const B &> >, std::__not_<std::__is_in_place_type<B> > >' requested here
enable_if_t<__and_<is_copy_constructible<_Tp>,
^
/opt/compiler-explorer/gcc-7.3.0/lib/gcc/x86_64-linux-gnu/7.3.0/../../../../include/c++/7.3.0/any:196:7: note: while substituting prior template arguments into non-type template parameter [with _ValueType = const B &, _Tp = B, _Mgr = std::any::_Manager_external<B>]
any(_ValueType&& __value)
^~~~~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-7.3.0/lib/gcc/x86_64-linux-gnu/7.3.0/../../../../include/c++/7.3.0/type_traits:973:28: note: while substituting deduced template arguments into function template 'any' [with _ValueType = const B &, _Tp = (no value), _Mgr = (no value), $3 = (no value)]
= decltype(::new _Tp(declval<_Arg>()))>
^
/opt/compiler-explorer/gcc-7.3.0/lib/gcc/x86_64-linux-gnu/7.3.0/../../../../include/c++/7.3.0/type_traits:974:24: note: in instantiation of default argument for '__test<B, const B &>' required here
static true_type __test(int);
^~~~~~~~~~~
/opt/compiler-explorer/gcc-7.3.0/lib/gcc/x86_64-linux-gnu/7.3.0/../../../../include/c++/7.3.0/type_traits:984:24: note: while substituting deduced template arguments into function template '__test' [with _Tp = B, _Arg = const B &, $2 = (no value)]
typedef decltype(__test<_Tp, _Arg>(0)) type;
^
/opt/compiler-explorer/gcc-7.3.0/lib/gcc/x86_64-linux-gnu/7.3.0/../../../../include/c++/7.3.0/type_traits:144:14: note: (skipping 9 contexts in backtrace; use -ftemplate-backtrace-limit=0 to see all)
: public conditional<_B1::value, _B2, _B1>::type
^
/opt/compiler-explorer/gcc-7.3.0/lib/gcc/x86_64-linux-gnu/7.3.0/../../../../include/c++/7.3.0/any:170:17: note: in instantiation of template class 'std::__and_<std::is_copy_constructible<B>, std::is_constructible<B, const B &> >' requested here
enable_if<__and_<is_copy_constructible<_Tp>,
^
/opt/compiler-explorer/gcc-7.3.0/lib/gcc/x86_64-linux-gnu/7.3.0/../../../../include/c++/7.3.0/any:175:5: note: in instantiation of template type alias '__any_constructible' requested here
using __any_constructible_t =
^
/opt/compiler-explorer/gcc-7.3.0/lib/gcc/x86_64-linux-gnu/7.3.0/../../../../include/c++/7.3.0/any:181:56: note: in instantiation of template type alias '__any_constructible_t' requested here
__any_constructible_t<_Tp, _ValueType&&> = true,
^
/opt/compiler-explorer/gcc-7.3.0/lib/gcc/x86_64-linux-gnu/7.3.0/../../../../include/c++/7.3.0/any:183:7: note: while substituting prior template arguments into non-type template parameter [with _ValueType = const B &, _Tp = B, _Mgr = std::any::_Manager_external<B>]
any(_ValueType&& __value)
^~~~~~~~~~~~~~~~~~~~~~~~~
<source>:63:23: note: while substituting deduced template arguments into function template 'any' [with _ValueType = const B &, _Tp = (no value), _Mgr = (no value), $3 = (no value), $4 = (no value)]
B(const B& b) : A(b) {
^
1 error generated.
Compiler returned: 1
Interestingly, it compiles in gcc-7.1 or higher.
Is the copy constructor for B calling the constructor of A instead of the copy constructor for A which is what I want to happen? If so, how do I make the B copy constructor work as I am intending it to do?
p.s. Here's a link to the online compiler I used for this https://godbolt.org/z/_22nuW.
If so, how do I make the B copy constructor work as I am intending it to do?
I think the simplest solution would be to replace B(const B& b) : A(b) {} with B(const B& b) = default; (or just removing this line). Then your program will be accepted by Clang 5, demo: https://gcc.godbolt.org/z/vzE3s16hs
The other option is to change standard library implementation from GNU libstdc++ to Clang's own libc++, which is done by -stdlib=libc++ command line option. Then your original code will be accepted as is, demo: https://godbolt.org/z/6ndTdGGE7

boost optional and user-defined conversion

I am unable to write a correct user defined conversion for a type Item. This is what I've tried:
#include <iostream>
#include <boost/optional.hpp>
struct A
{
int x;
};
struct Item
{
boost::optional<int> x_;
Item(){}
Item(const A& s)
: x_(s.x)
{
}
operator boost::optional<A>() const {
boost::optional<A> s;
if (x_) {
s->x = *x_;
}
return s;
}
};
std::vector<A> getA(const std::vector<Item> &items) {
std::vector<A> a;
for (const auto &i : items) {
if (i.x_) {
a.push_back(*static_cast<boost::optional<A>>(i)); // <- this line causes error
}
}
return a;
}
That is how I use it:
int main() {
A a;
a.x = 3;
Item i(a);
auto v = getA({i});
return 0;
}
g++ -std=c++11 says:
In file included from /usr/include/boost/optional.hpp:15:0,
from test.cpp:2:
/usr/include/boost/optional/optional.hpp: In instantiation of ‘void boost::optional_detail::optional_base<T>::construct(const Expr&, const void*) [with Expr = Item; T = A]’:
/usr/include/boost/optional/optional.hpp:262:25: required from ‘boost::optional_detail::optional_base<T>::optional_base(const Expr&, const Expr*) [with Expr = Item; T = A]’
/usr/include/boost/optional/optional.hpp:559:78: required from ‘boost::optional<T>::optional(const Expr&) [with Expr = Item; T = A]’
test.cpp:30:55: required from here
/usr/include/boost/optional/optional.hpp:392:8: error: no matching function for call to ‘A::A(const Item&)’
new (m_storage.address()) internal_type(expr) ;
^
/usr/include/boost/optional/optional.hpp:392:8: note: candidates are:
test.cpp:3:8: note: A::A()
struct A
^
test.cpp:3:8: note: candidate expects 0 arguments, 1 provided
test.cpp:3:8: note: constexpr A::A(const A&)
test.cpp:3:8: note: no known conversion for argument 1 from ‘const Item’ to ‘const A&’
test.cpp:3:8: note: constexpr A::A(A&&)
test.cpp:3:8: note: no known conversion for argument 1 from ‘const Item’ to ‘A&&’
Why does it try to find A struct constructor instead of use user defined conversion operator?
You may point me directly to any position of the user-defined conversion page because I am unable to find any reason for this. For example,
User-defined conversion function is invoked on the second stage of the implicit conversion, which consists of zero or one converting constructor or zero or one user-defined conversion function.
in my opinion directly says that if no conversion constructor is defined then user-defined conversion function will be used. Am I wrong? And if yes, how can I implement user-defined conversion then without defining conversion cunstructor in struct A ?
You have two issues with your code. Your optional operator never initializes the boost::optional. If you don't do that, accessing members is undefined behavior. What you have to do is:
operator boost::optional<A>() const {
boost::optional<A> s;
if (x_) {
s = A{*x_};
}
return s;
}
The second issue is when you do:
static_cast<boost::optional<A>>(i);
That is equivalent to:
boost::optional<A> __tmp(i);
But it turns out that boost::optional has an explicit template constructor. That will be preferred to your conversion function. The error you're seeing is the compiling going down the path of this factory constructor, where Item is not such a factory.
You could simply use boost::optional<A> directly:
std::vector<A> getA(const std::vector<Item> &items) {
std::vector<A> a;
for (boost::optional<A> opt : items) {
if (opt) {
a.push_back(*opt);
}
}
return a;
}
Or, since the constructor template is explicit, you could use the conversion operator in a non-explicit context:
boost::optional<A> opt = i;
a.push_back(*opt);
This has the added benefit of also being easier to read.

std::swap for non copiable but movable struct

According to C++ reference std::swap is equivalent to
T c(std::move(a)); a=std::move(b); b=std::move(c);
This should allow to swap two non copiable but movable object. Therefore I don't understand why
#include<utility>
struct Foo {
Foo() = delete;
Foo(int) {};
Foo(Foo &) = delete;
Foo(Foo &&) {};
~Foo() {};
};
int main() {
Foo a(1),b(2);
std::swap(a,b);
}
is refused by the compiler with
In file included from /usr/include/c++/4.8/bits/stl_pair.h:59:0,
from /usr/include/c++/4.8/utility:70,
from swap.cpp:1:
/usr/include/c++/4.8/bits/move.h: In instantiation of ‘void std::swap(_Tp&, _Tp&) [with _Tp = Foo]’:
swap.cpp:13:16: required from here
/usr/include/c++/4.8/bits/move.h:176:11: error: use of deleted function ‘Foo& Foo::operator=(const Foo&)’
__a = _GLIBCXX_MOVE(__b);
^
swap.cpp:3:8: note: ‘Foo& Foo::operator=(const Foo&)’ is implicitly declared as deleted because ‘Foo’ declares a move constructor or move assignment operator
struct Foo {
^
In file included from /usr/include/c++/4.8/bits/stl_pair.h:59:0,
from /usr/include/c++/4.8/utility:70,
from swap.cpp:1:
/usr/include/c++/4.8/bits/move.h:177:11: error: use of deleted function ‘Foo& Foo::operator=(const Foo&)’
__b = _GLIBCXX_MOVE(__tmp);
note: this is with GCC 4.8 and 4.9 but clang complain as well.
You declared a move constructor. However, you need a move assignment operator for std::swap. You should add the following two operators:
auto operator=(const Foo& rhs) & -> Foo& = delete;
auto operator=(Foo&& rhs) & noexcept -> Foo&
{
// ...
return *this;
}