I'm attempting to create a base class for creating helpers around various scoped operations, and to do so the base class allows the assignment of a callable. A simple predicate determines whether or not the callable is invoked. Regardless, the dtor is always invoked.
The catch is that each derived class needs to explicitly inherit the operator=. Is there a way to write the base class so that this is not required?
#include <functional>
#include <iostream>
using callback_t = std::function<void(void)>;
struct C
{
constexpr C(bool ok) : ok_{ok} {}
bool ok_ {false};
bool operator=(callback_t fn) const noexcept {
return ok_ ? fn(), true : false;
}
~C() noexcept {
std::cout << "end\n";
}
};
struct D final : public C
{
using C::operator=;
};
int main()
{
auto predicate = [] { return true; };
D{predicate()} = [] {
std::cout << "ok\n";
};
D{false} = [] {
std::cout << "wrong\n";
};
}
(also https://gcc.godbolt.org/z/eh8ncffos)
Removing the line from struct D
using C::operator=;
causes compile errors since Ds default copy-operator preempts C's custom operator=.
<source>: In function 'int main()':
<source>:24:9: error: no match for 'operator=' (operand types are 'D' and 'main()::<lambda()>')
24 | };
| ^
<source>:14:12: note: candidate: 'constexpr D& D::operator=(const D&)'
14 | struct D final : public C
| ^
<source>:14:12: note: no known conversion for argument 1 from 'main()::<lambda()>' to 'const D&'
<source>:14:12: note: candidate: 'constexpr D& D::operator=(D&&)'
<source>:14:12: note: no known conversion for argument 1 from 'main()::<lambda()>' to 'D&&'
<source>:27:9: error: no match for 'operator=' (operand types are 'D' and 'main()::<lambda()>')
27 | };
| ^
<source>:14:12: note: candidate: 'constexpr D& D::operator=(const D&)'
14 | struct D final : public C
| ^
<source>:14:12: note: no known conversion for argument 1 from 'main()::<lambda()>' to 'const D&'
<source>:14:12: note: candidate: 'constexpr D& D::operator=(D&&)'
<source>:14:12: note: no known conversion for argument 1 from 'main()::<lambda()>' to 'D&&'
The catch is that each derived class needs to explicitly inherit the operator=. Is there a way to write the base class so that this is not required?
No, this isn't possible.
The rule is that functions in different scopes do not overload. Since the derived class will always have something named operator= (either the derived class declares one itself, or the copy assignment operator is declared implicitly... even if it would be defined as deleted). So when you do derived = x;, name lookup for operator= will start in Derived and, having found candidates, stop there. It won't keep going to look into the base class... unless you bring in that candidate with a using-declaration like you demonstrated the question.
The only way to still use operator= but stash the functionality somewhere would be to actually invert your class hierarchy. Instead of:
struct C {
// ... implementation here ...
};
struct D : C {
using C::operator=;
};
// use D
you can do:
template <typename Derived>
struct C {
// implementation here
};
struct D { }
// use C<D>
This way, C is actually the class you're interacting with, so its operator= would be the first candidate considered.
Alternatively, if you simply don't use = as the spelling for this operation, every other operator is available and they would not need a using-declaration in the derived class to work.
Given the comment about wishing to avoid parentheses, your list of binary operators that would not require one are: ->*, +, -, *, /, %, ^, &, |, &&, ||, <, >, <<, and >> (plus the compound-assignment versions of those).
I'm deliberately excluding == and <=> from the list because those are special and just don't.
Related
I am trying to get a simple example to work to understand how to use std::enable_if, here is the problem:
I am reading the textbook C++ Templates The Complete Guide by David Vandevoorde, Nicolai M.Josuttis, Chapter 6, Section 5.
This chapter mentions: "std::enable_if to prevent being able to copy objects of a class template C<> if the template parameter is an integral type", and its following code:
template <typename T> class C {
public:
// user-define the predefined copy constructor as deleted (with conversion to
// volatile to enable better matches)
C(C const volatile &) = delete;
// if T is not integral type, provide copy constructor template with better match:
template <typename U,
typename = std::enable_if_t<!std::is_integral<U>::value>>
C(C<U> const &) {
std::cout << "tmpl copy constructor" << std::endl;
}
};
My question is, how should the above code be called and used?
for example, I tried:
C<int> c_int;
std::string s = "sname";
C<std::string> c_string1(std::string);
C<std::string> c_string2(c_string1);
But give me compile error:
specialmember3.cc:22:39: error: no matching function for call to ‘C<std::__cxx11::basic_string<char> >::C(C<std::__cxx11::basic_string<char> > (&)(std::string))’
22 | C<std::string> c_string2(c_string1);
| ^
specialmember3.cc:14:3: note: candidate: ‘template<class U, class> C<T>::C(const C<U>&)’
14 | C(C<U> const &) {
| ^
specialmember3.cc:14:3: note: template argument deduction/substitution failed:
specialmember3.cc:22:39: note: mismatched types ‘const C<U>’ and ‘C<std::__cxx11::basic_string<char> >(std::string)’ {aka ‘C<std::__cxx11::basic_string<char> >(std::__cxx11::basic_string<char>)’}
22 | C<std::string> c_string2(c_string1);
| ^
specialmember3.cc:9:3: note: candidate: ‘constexpr C<T>::C() [with T = std::__cxx11::basic_string<char>]’
9 | C() = default;
| ^
specialmember3.cc:9:3: note: candidate expects 0 arguments, 1 provided
Can someone please give me some hints or code guidance on how to use above template constructor?
You have declared c_string1 as a function. I think you meant this
C<std::string> c_string1;
C<std::string> c_string2(c_string1);
That class, as it is defined, is somewhat useless.
a) You can't default-initialize it, default constructor is removed. C<int> c_int; is ill-formed.
b) You can't create it from value, e.g. C<std::string> c_string1(some_str);, because that constructor does not exist.
Essentially you can copy it, but you cannot create it, which is a nonsense. It breaks rule of 3/5/0.
template <typename T>
class C {
public:
C() /*Initialization here */ {}
C(const T& val) /*Initialization here */ {
std::cout << "tmpl copy constructor: " << val << std::endl;
}
// user-define the predefined copy constructor as deleted (with conversion to
// volatile to enable better matches)
C(C const volatile &) = delete;
// if T is not integral type, provide copy constructor template with better match:
template <typename U,
typename = std::enable_if_t<!std::is_integral<U>::value>>
C(C<U> const &) {
std::cout << "tmpl copy constructor" << std::endl;
}
};
In that case those would be legal:
C<int> c_int;
std::string s = "sname";
C<std::string> c_string1(s);
C<std::string> c_string2(c_string1);
THe copy template defined disallows copying of C if T is integral, so
C<int> c_int2 (c_int); // use of deleted function 'C<T>::C(const volatile C<T>&)
Long story short: I'd like to understand why the D::operator B() const conversion operator is not used in the last line in the code below, which thus fails when compiling with g++ -std=c++17 source.cpp (compiling with g++ -std=c++2a deleteme.cpp is successful, though).
The error is:
$ g++ -std=c++17 deleteme.cpp && ./a.out
In file included from /usr/include/c++/10.2.0/cassert:44,
from deleteme.cpp:1:
deleteme.cpp: In function ‘int main()’:
deleteme.cpp:19:14: error: no match for ‘operator==’ (operand types are ‘D’ and ‘B’)
19 | assert(d == B{2}); // conversion operator not invoked explicitly errors // LINE
| ~ ^~ ~~~~
| | |
| D B
In file included from /usr/include/c++/10.2.0/utility:70,
from deleteme.cpp:2:
/usr/include/c++/10.2.0/bits/stl_pair.h:466:5: note: candidate: ‘template<class _T1, class _T2> constexpr bool std::operator==(const std::pair<_T1, _T2>&, const std::pair<_T1, _T2>&)’
466 | operator==(const pair<_T1, _T2>& __x, const pair<_T1, _T2>& __y)
| ^~~~~~~~
/usr/include/c++/10.2.0/bits/stl_pair.h:466:5: note: template argument deduction/substitution failed:
In file included from /usr/include/c++/10.2.0/cassert:44,
from deleteme.cpp:1:
deleteme.cpp:19:20: note: ‘B’ is not derived from ‘const std::pair<_T1, _T2>’
19 | assert(d == B{2}); // conversion operator not invoked explicitly errors // LINE
|
The code is:
#include <cassert>
#include <utility>
struct B {
int x;
B(int x) : x(x) {}
bool operator==(B const& other) const { return x == other.x; }
};
struct D : std::pair<B,char*> {
operator B() const { return this->first; }
};
int main() {
B b{1};
D d{std::pair<B,char*>(B{2},(char*)"hello")};
assert((B)d == B{2}); // conversion operator invoked explicitly is fine
assert(d == B{2}); // conversion operator not invoked explicitly errors // LINE
}
This question is a follow up to this. There I got help to write a class Recursive which behaves like a pair (so inherits from it) whose first is a std::array and whose second is a boost::hana::optional<std::vector<...>> (see the link for details).
Since the second of the std::pair is kind of an "advanced" information to what's contained in the first, in many places I'd like to cast/convert this object of std::pair-like class Recursive to the type of its first.
When compiler sees d == B{2}, it first creates a list of operator== overloads that it's able to find, and then performs overload resolution on them.
As the link explains, the overload list contains:
Member operator==s of the first operand, if any.
Non-member operators==s found by unqualified lookup, if any.
Built-in operator==s, if your operands can be converted to built-in types.
There's no mention of examining conversion operators of the first operand, so your operator== doesn't get found.
The solution is to make the operator== non-member (possibly define it as a friend). This works:
friend bool operator==(const B &a, const B &b) {return a.x == b.x;}
Starting from C++20, the rules of comparison operators got relaxed: the compiler will look for member operator== in the second operand as well, and will happily call non-member operator== with arguments in a wrong order.
So a == b and b == a are now equivalent to a degree, except that:
An operator== called in this new manner must return bool.
If given choice, the compiler will prefer the old ways of calling operator== over calling the member operator== of the rhs, which is in turn preferred over calling a non-member operator== with a wrong argument order.
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.
I have this simplified code consisting of a class with a static function, which is stored in map:
#include <iostream>
#include <functional>
#include <map>
class A {
public:
static void f(const std::string &s) { std::cout << s; }
};
std::map<std::string, std::function<void(std::string const &)>> fs;
int main() {
fs["f"] = &A::f;
fs["f"]("hello");
}
This prints the expected hello.
The problem occurs if I overload f() with:
static void f(const std::string &s, int c) { while(c-->0) { std::cout << s; } }
This results in the error:
error: no viable overloaded '='
fs["f"] = &A::f;
~~~~~~~ ^ ~~~~~
/usr/bin/../lib/gcc/x86_64-linux-gnu/4.9/../../../../include/c++/4.9/functional:2241:7: note: candidate function not viable: no overload of 'f' matching 'const std::function<void (const std::basic_string<char> &)>' for 1st argument
operator=(const function& __x)
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/4.9/../../../../include/c++/4.9/functional:2259:7: note: candidate function not viable: no overload of 'f' matching 'std::function<void (const std::basic_string<char> &)>' for 1st argument
operator=(function&& __x)
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/4.9/../../../../include/c++/4.9/functional:2273:7: note: candidate function not viable: no overload of 'f' matching 'nullptr_t' for 1st argument
operator=(nullptr_t)
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/4.9/../../../../include/c++/4.9/functional:2302:2: note: candidate template ignored: couldn't infer template argument '_Functor'
operator=(_Functor&& __f)
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/4.9/../../../../include/c++/4.9/functional:2311:2: note: candidate template ignored: couldn't infer template argument '_Functor'
operator=(reference_wrapper<_Functor> __f) noexcept
^
However, calling both functions works:
A::f("hello ");
A::f("echo ", 3);
So, my question are:
Why this code not compiling even though the operator= seems to exist and function if I don't overload f()?
How can I get it to work without giving both functions different names?
Why this code not compiling even though the operator= seems to exist
and function if I don't overload f()?
Because the compiler doesn't know which overload to choose. How could he? There is no criterion upon which he can decide which one is suited better. Every std::function allows arbitrary function objects to be assigned and doesn't check any signatures. If you wanted to save only function pointers of this particular signature you should have declared the map appropriately.
How can I get it to work without giving both functions different
names?
As already mentioned it works by casting the expression to a function pointer of the specific type.
fs["f"] = static_cast<void(*)(std::string const&)>( &A::f );
This way no ambiguities arise; There is exactly one overload that can be casted to this function to pointer type. If this appears more often then a typedef could be feasible.
Or a little helper class template:
template <typename... Exact>
struct funptr
{
template <typename R>
constexpr auto operator()(R(*p)(Exact...)) -> decltype(p)
{ return p; }
};
fs["f"] = funptr<std::string const&>()(&A::f);
Demo.
Given the following source code:
#include <memory>
#include <iostream>
using namespace std;
struct concept
{
virtual void perform() = 0;
};
struct model : concept, enable_shared_from_this<model>
{
void perform() override {
cout << "my pointer is " << shared_from_this().get() << endl;
}
};
int main(int argc, const char * argv[])
{
// shared_ptr<concept> concept_ptr = make_shared<model>();
shared_ptr<concept> concept_ptr { new model };
concept_ptr->perform();
return 0;
}
Compiling under gcc, this code compiles and associates the internal weak_ptr with the address of model.
Under clang the code will not compile (error message included at the end)
If you replace the initialisation of concept_ptr with shared_ptr<concept> concept_ptr = make_shared<model>(); it will compile on both.
Which is correct?
edit:
My version of clang is the one that ships with Xcode:
$ clang --version
Apple LLVM version 5.1 (clang-503.0.40) (based on LLVM 3.4svn)
Target: x86_64-apple-darwin13.3.0
Thread model: posix
edit2:
Just wanted to say thanks to everyone for contributing.
If you're interested, the reason I want to do this is that I want an opaque interface to an implementation with shared-handle semantics. Some implementations (async ones) require that callback objects ensure that the implementation object still exists (argues for shared_from_this and weak_ptr::lock). Other implementations do not require this. I wanted to avoid encumbering the concept (public interface) with the enable_shared_from_this<> base class, since that couples implementation with interface - a known evil.
In most cases, it's reasonable to use make_shared to create the implementation object. In rarer cases that require a custom destructor, the following seems portable:
auto concept_ptr = static_pointer_cast<concept>(shared_ptr<model> {
new model ,
[](model* self) {
// some_deletion_operation on self;
} });
appendix:
error message on clang:
In file included from /Users/richardh/Documents/dev/Scratchpad/tryit/tryit/try2.cpp:1:
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/c++/v1/memory:4013:35: error: no viable overloaded '='
__e->__weak_this_ = *this;
~~~~~~~~~~~~~~~~~ ^ ~~~~~
...etc...
I understand that libstdc++ follows the standard more closely here.
Concerning the requirements for
shared_ptr<T> shared_from_this();
shared_ptr<const T> shared_from_this() const;
both N3337 §20.7.2.4 (7) and N3936 §20.8.2.5 (7) only require
enable_shared_from_this<T> shall be an accessible base class
of T. *this shall be a subobject of an object t of type T. There shall
be at least one shared_ptr instance p that owns &t.
There is no requirement named that one shared_ptr owning &t actually has to be a shared_ptr<T> or shared_ptr<A_to_T_Convertible>.
And that very function is the core of that class' functionality.
Thus, given Tp as the actual param of the enabled_shared_from_this and Tp1 as the actual parameter of that owning shared_ptr, is_convertible<Tp1, Tp>::value == true, let alone is_same<Tp1, Tp>::value == true, is not required by the standard, same for respective pointers.
And indeed, the full output of clang++ using libc++ has
/usr/local/bin/../include/c++/v1/memory:3997:35: error: no viable overloaded '='
__e->__weak_this_ = *this;
~~~~~~~~~~~~~~~~~ ^ ~~~~~
/usr/local/bin/../include/c++/v1/memory:4035:5: note: in instantiation of
function template specialization
'std::__1::shared_ptr<concept>::__enable_weak_this<model>' requested here
__enable_weak_this(__p);
^
[...]enable_shared.cxx:34:25: note: in instantiation
of function template specialization
'std::__1::shared_ptr<concept>::shared_ptr<model>' requested here
shared_ptr<concept> model_ptr1(new model);
^
/usr/local/bin/../include/c++/v1/memory:4942:15: note: candidate function not
viable: no known conversion from 'std::__1::shared_ptr<concept>' to 'const
std::__1::weak_ptr<model>' for 1st argument
weak_ptr& operator=(weak_ptr const& __r) _NOEXCEPT;
^
/usr/local/bin/../include/c++/v1/memory:4953:15: note: candidate function not
viable: no known conversion from 'std::__1::shared_ptr<concept>' to
'std::__1::weak_ptr<model>' for 1st argument
weak_ptr& operator=(weak_ptr&& __r) _NOEXCEPT;
^
/usr/local/bin/../include/c++/v1/memory:4949:9: note: candidate template
ignored: could not match 'weak_ptr' against 'shared_ptr'
operator=(weak_ptr<_Yp> const& __r) _NOEXCEPT;
^
/usr/local/bin/../include/c++/v1/memory:4960:9: note: candidate template
ignored: could not match 'weak_ptr' against 'shared_ptr'
operator=(weak_ptr<_Yp>&& __r) _NOEXCEPT;
^
/usr/local/bin/../include/c++/v1/memory:4967:13: note: candidate template
ignored: disabled by 'enable_if' [with _Yp = concept]
is_convertible<_Yp*, element_type*>::value,
^
So libc++ here wants
is_convertible<Tp1* /*= Base* = concept**/, Tp* /*= Derived* = model* */>
which of course fails here, that the run-time *this of that very shared_ptr<Tp1> would be dynamic_cast-able to Tp* is out of ansatz here.
From this perspective, it's also clear why shared_ptr<concept> concept_ptr = make_shared<model>(); doesn't suffer from that; on the rhs there is a shared_ptr<Tp /* = derived = model */> constructor and for that ptr is_convertible holds.
libstdc++ doesn't suffer from this, because it passes the argument, thus its type (= Derived = model), of the shared_ptr<Tp1 /* = Base = concept*/> constructor down to the internal weak_ptr<T /*= Derived = model*/> assignment, not the shared_ptr in construction.
https://github.com/mirrors/gcc/blob/master/libstdc%2B%2B-v3/include/bits/shared_ptr_base.h#L848
template<typename _Tp, _Lock_policy _Lp>
class __shared_ptr
{
https://github.com/mirrors/gcc/blob/master/libstdc%2B%2B-v3/include/bits/shared_ptr_base.h#L858
template<typename _Tp1>
explicit __shared_ptr(_Tp1* __p)
: _M_ptr(__p), _M_refcount(__p)
{
__glibcxx_function_requires(_ConvertibleConcept<_Tp1*, _Tp*>)
static_assert( !is_void<_Tp1>::value, "incomplete type" );
static_assert( sizeof(_Tp1) > 0, "incomplete type" );
__enable_shared_from_this_helper(_M_refcount, __p, __p);
}
https://github.com/mirrors/gcc/blob/master/libstdc%2B%2B-v3/include/bits/shared_ptr_base.h#L1459
template<typename _Tp, _Lock_policy _Lp>
class __enable_shared_from_this
{
https://github.com/mirrors/gcc/blob/master/libstdc%2B%2B-v3/include/bits/shared_ptr_base.h#L1482
private:
template<typename _Tp1>
void
_M_weak_assign(_Tp1* __p, const __shared_count<_Lp>& __n) const noexcept
{ _M_weak_this._M_assign(__p, __n); }
template<typename _Tp1>
friend void
__enable_shared_from_this_helper(const __shared_count<_Lp>& __pn,
const __enable_shared_from_this* __pe,
const _Tp1* __px) noexcept
{
if (__pe != 0)
__pe->_M_weak_assign(const_cast<_Tp1*>(__px), __pn);
}
My point of view only; comments welcome.
#Richard Hodges: +1, very interesting topic