#include<iostream>
#include<utility>
using namespace std;
struct A
{
void set(const int &){}
void set(int &&) noexcept
{}
};
template<class Assign,class T,class Func>
struct B
{
B& operator=(const Assign &)
{
return *this;
}
B& operator=(Assign &&) noexcept(noexcept(declval<T>().set(declval<Assign>())))
{
return *this;
}
};
int main()
{
cout<<is_nothrow_assignable<B<int,A,void(A::*)(int &&)>&,int&&>::value<<endl;
}
I want to make the line
B& operator=(Assign &&) noexcept(noexcept(declval<T>().set(declval<Assign>())))
to
B& operator=(Assign &&) noexcept(noexcept(declval<T>().Func(declval<Assign>())))
(But it occurs compilation error.)
So that user can specify which member function should be used.
Is there any possible to do that without knowing which member function should be called in advance?
If you use another parameter to specify the function, it'll work.
template<class Assign,class T,class Func, Func f> struct B
//...
B& operator=(Assign &&) noexcept(noexcept((declval<T>().*f)(declval<Assign>())))
//...
cout<<is_nothrow_assignable<B<int,A,void(A::*)(int &&), &A::set>&,int&&>::value<<endl;
Add a Func non-type parameter to B, use member function pointer call syntax in the noexcept operator, and then you can specify the function by passing a pointer to it.
Full code here, if it needs more context.
Related
Consider the following C++ code with my failed attempt to avoid preference of non-template copy&move constructors and assignment operators:
template<typename T> class A {
public:
A() { /* implementation here */ }
// Remove from the overloads the default copy&move constructors and assignment operators
A(const A&) = delete;
A& operator=(const A&) = delete;
A(A&&) = delete;
A& operator=(A&&) = delete;
// I want these to be used e.g. by std::vector
template<typename U> A(const A<U>& fellow) { /* implementation here */ }
template<typename U> A& operator=(const A<U>& fellow) { /* implementation here */ }
template<typename U> A(A<U>&& fellow) { /* implementation here */ }
template<typename U> A& operator=(A<U>&& fellow) { /* implementation here */ }
};
However, I get the following error
attempting to reference a deleted function
when trying to push A items to a vector or simply copy-construct like:
A<int> a1{};
A<int> a2(a1);
UPDATE1: I need template copy&move constructors and assignment operators, because the template argument really just controls some caching, so A<T1> can be safely assigned to A<T2>.
You can make compiler happy by declaring deleted copy constructor / assignment operator with alternative signature which will not cause this overload to be selected but will prevent generation of constructor / assignment operator by compiler:
template<typename T> class A
{ public:
A() { /* implementation here */ }
// Remove from the implicit declaration of the default copy&move constructors and assignment operators
A(A volatile const &) = delete;
A & operator =(A volatile const &) = delete;
// I want these to be used e.g. by std::vector
template<typename U> A(A<U> const & fellow) { /* implementation here */ }
template<typename U> A & operator =(A<U> const & fellow) { /* implementation here */ return *this;}
template<typename U> A(A<U> && fellow) { /* implementation here */ }
template<typename U> A & operator =(A<U> && fellow) { /* implementation here */ return *this; }
};
int main()
{
A<int> a1{};
A<int> a2{a1};
return 0;
}
online compiler
15.8.1 Copy/move constructors [class.copy.ctor]
1. A non-template constructor for class X is a copy constructor if its first parameter is of type X&, const X&, volatile X& or const volatile X&, and either there are no other parameters or else all other parameters have default arguments
A minimal example of a copy constructor that delegate the execution to the template constructor using a second unused (and defaulted) argument
#include <iostream>
template <typename T>
struct A
{
A()
{ }
A (A const & a0) : A{a0, 0}
{ }
template<typename U>
A (A<U> const &, int = 0)
{ std::cout << "template constructor" << std::endl; }
};
int main()
{
A<int> a0;
A<int> a1{a0};
}
-- EDIT --
The OP asks
What about operator=? Trying to add a dummy parameter gives compiler errors binary 'operator =' has too many parameters and 'operator =' cannot have default parameters
For operator=() I propose to "delegate" (not in the meaning of delegating constructor, in this case) both operators to a normal method; a template one.
Something as
template <typename U>
A & assign (A<U> const &)
{ /* do assignment */ return *this; }
A & operator= (A const & a0)
{ return assign(a0); }
template <typename U>
A & operator= (A<U> const & a0)
{ return assign(a0); }
Maybe the assign() method can be a private one.
Or better, as suggested by Jarod42 (thanks), directly calling the template operator from the not-template one
template <typename U>
A & operator= (A<U> const & a0)
{ /* do assignment */ return *this; }
A & operator= (A const & a0)
{ return operator=<T>(a0); }
The following code doesn't work. I don't know why. Does that has anything to do with implicit/explicit? Is that called a conversion?
#include <type_traits>
template<typename T>
class A {
public:
A(T x) {_x=x;}
template<typename T2> explicit A(const A<T2> &r) {}
template<typename T2> explicit A(A<T2> &r){}
template<typename T2>
void operator=(const A<T2>& rhs) { _x = rhs._x; }
template<typename T2>
void operator=(A<T2>& rhs) { _x = rhs._x; }
T _x;
};
int main() {
const A<int> a(10);
A<int> b = a;
b = A<int>(5);
A<int> c(a);
b(a); // not working. why?
}
Error: g++ 6
test.cpp: In function βint main()β:
test.cpp:25:12: error: no match for call to β(A<int>) (const A<int>&)β
b(a); // not working. why?
I'm not sure what you expect b(a); to do but it is not going to do what you want. An object can only be constructed once. After it has been constructed you cannot reconstruct it. What you have when you do b(a); is you try to call the operator() of the class. Since you do not have one you get a compiler error. If you want to set b to the value of a then you need
b = a;
How does this templated operator() work in reference_wrapper implementation
template <class T>
class reference_wrapper {
public:
// types
typedef T type;
// construct/copy/destroy
reference_wrapper(T& ref) noexcept : _ptr(std::addressof(ref)) {}
reference_wrapper(T&&) = delete;
reference_wrapper(const reference_wrapper&) noexcept = default;
// assignment
reference_wrapper& operator=(const reference_wrapper& x) noexcept = default;
// access
operator T& () const noexcept { return *_ptr; }
T& get() const noexcept { return *_ptr; }
here it goes:
template< class... ArgTypes >
typename std::result_of<T&(ArgTypes&&...)>::type
operator() ( ArgTypes&&... args ) const {
return std::invoke(get(), std::forward<ArgTypes>(args)...);
}
why do we need operator() anyway? how it works?
what is the return content "result_of::type"?
what is (ArgTypes && ..) ??
invoke(get) ???
this code looks like C++ from another planet :)
private:
T* _ptr;
};
why do we need operator() anyway? how it works?
Suppose following context
int foo(int bar)
{
return bar + 5;
}
int main()
{
std::reference_wrapper<int(int)> ref = foo;
ref(5);
}
ref(5) calls operator() of reference wrapper. If it wouldn't be there, it would not work, because user defined conversion wouldn't happen in this case.
operator() returns std::result_of<T&(ArgTypes&&...), which is return value of the function stored and std::invoke call such function and forwards parameters to it.
#include <iostream>
template <typename T1, typename T2>
bool func(const T1& t, const T2& t2) {
return t == t2;
}
class Base {
public:
bool operator ==(const Base&) const { return true;}
Base(int y) : x(y) {}
operator int() {
return x;
}
int x;
};
int main() {
func<long, Base>(4L, Base(5)); // not ok
func<long, long>(4L, Base(5)); //ok
}
Can somebody elaborate why the first version does not work? In otherwords why does the binary operator == in func not use the conversion operator int to convert the template parameter bound to Base into int?
Is there anyway to make version 1 work by only modifying the class Base?
Your func accepts its parameters by const reference, but the operator int() defined in your Base class is a non-const member function. Mark it as a const member function, as shown below, and your code will compile:
#include <iostream>
template <typename T1, typename T2>
bool func(const T1& t, const T2& t2) {
return t == t2;
}
class Base {
public:
bool operator ==(const Base&) const { return true;}
Base(int y) : x(y) {}
operator int() const {
return x;
}
int x;
};
int main() {
func<long, Base>(4L, Base(5)); // ok now!
func<long, long>(4L, Base(5)); // ok
}
Live example
For example:
struct B{};
struct A {
const B& findB() const { /* some non trivial code */ }
// B& findB() { /* the same non trivial code */ }
B& findB() {
const A& a = *this;
const B& b = a.findB();
return const_cast<B&>(b);
}
};
The thing is I want to avoid repeating the same logic inside the constant findB and non-constant findB member function.
Yes, you can cast the object to const, call the const version, then cast the result to non-const:
return const_cast<B&>(static_cast<const A*>(this)->findB());
Casting away const is safe only when the object in question was not originally declared const. Since you are in a non-const member function, you can know this to be the case, but it depends on the implementation. Consider:
class A {
public:
A(int value) : value(value) {}
// Safe: const int -> const int&
const int& get() const {
return value;
}
// Clearly unsafe: const int -> int&
int& get() {
return const_cast<int&>(static_cast<const A*>(this)->get());
}
private:
const int value;
};
Generally speaking, my member functions are short, so the repetition is tolerable. You can sometimes factor the implementation into a private template member function and call that from both versions.
I think, that using cast here is ok, but if you definitely want to avoid it, you can use some template magic:
struct B
{
B(const B&)
{
std::cout << "oops I copied";
}
B(){}
};
struct A {
public:
A(){}
A(const A&){ std::cout << "a is copied:(\n";}
const B& findB() const { return getter(*this); }
B& findB() { return getter(*this); }
private:
template <typename T, typename V>
struct same_const
{
typedef V& type;
};
template <typename T, typename V>
struct same_const<const T, V>
{
typedef const V& type;
};
template <typename T>
static typename same_const<T,B>::type getter(T& t) { return t.b;}
B b;
};
int main()
{
A a;
const A a_const;
const B& b1 = a.findB();
B& b2 = a.findB();
const B& b3 = a_const.findB();
//B& b4 = a_const.findB();
}