I have implemented a simple program in C++17, in which I try to call the base class operator= from a derived class using parameter expansion. But the program does not compile.
#include "pch.h"
#include <iostream>
class D
{
public:
D()
{
std::cout << "D ctror" << std::endl;
}
D & operator = (const D & other)
{
return *this;
}
};
class C
{
public:
C()
{
std::cout << "C ctror" << std::endl;
}
C & operator = (const C & other)
{
return *this;
}
};
class B
{
public:
B()
{
std::cout << "B ctror" << std::endl;
}
B & operator = (const B & other)
{
std::cout << "operator B" << std::endl;
return *this;
}
};
template<typename... Ts> class A: public Ts...
{
public:
A(): Ts()...
{
}
A & operator = (const A & other)
{
Ts::operator =(other);
return *this;
}
};
int main()
{
A<B,C,D> a1;
A<B,C,D> a2;
a1 = a2;
}
The toolset that is used is the Visual Studio 2017 (v141)
The error that is generated is the following
error C3520: '=': parameter pack must be expanded in this context
note: while compiling class template member function 'A
&A::operator =(const A &)' note: see reference to
function template instantiation 'A &A::operator =(const
A &)' being compiled note: see reference to class template
instantiation 'A' being compiled
You need to expand the parameter pack. How about a nice fold expression:
(Ts::operator=(other), ...);
This will expand Ts... and effectively create multiple calls to operator=, one for each type in the pack.
Related
In C++ (14) I have a template class with a type T for which I would like to use a class as the type. The class that I want to use for the type requires a parameter in its constructor... so how do I pass this parameter through the template instantiation?
My example code (template_hell.cpp):
#include <iostream>
template <typename T>
class my_template
{
public:
struct Stored_Data_Type
{
T data;
int set_count = 0;
};
my_template() : m_data() {};
T& write()
{
m_data.set_count++;
return m_data.data;
}
const T& get() const {return m_data.data;}
private:
Stored_Data_Type m_data;
};
class a_class
{
public:
a_class(int init): m_data(init) {};
void set(const int data) {m_data = data;};
const int get() const {return m_data;};
private:
int m_data;
};
class b_class : public a_class
{
public:
b_class(): a_class{0} {};
};
int main()
{
//a_class b(1);
b_class b;
b.set(2);
std::cout << "b: " << b.get() << std::endl;
my_template<int> my_int;
my_int.write() = 10;
std::cout << "my_int: " << my_int.get() << std::endl;
my_template<b_class> my_b;
my_b.write().set(2);
std::cout << "my_b: " << my_b.get().get() << std::endl;
// Compile error here:
my_template<a_class> my_a;
my_a.write().set(3);
std::cout << "my_a: " << my_a.get().get() << std::endl;
}
This all works and is fine until I add in the my_a template instance. Then I get this compile error:
template_hell.cpp: In instantiation of 'my_template<T>::my_template() [with T = a_class]':
template_hell.cpp:62:24: required from here
template_hell.cpp:13:26: error: use of deleted function 'my_template<a_class>::Stored_Data_Type::Stored_Data_Type()'
my_template() : m_data() {};
^
template_hell.cpp:7:10: note: 'my_template<a_class>::Stored_Data_Type::Stored_Data_Type()' is implicitly deleted because the default definition would be ill-formed:
struct Stored_Data_Type
^~~~~~~~~~~~~~~~
template_hell.cpp:7:10: error: no matching function for call to 'a_class::a_class()'
template_hell.cpp:31:3: note: candidate: a_class::a_class(int)
a_class(int init): m_data(init) {};
^~~~~~~
template_hell.cpp:31:3: note: candidate expects 1 argument, 0 provided
template_hell.cpp:27:7: note: candidate: constexpr a_class::a_class(const a_class&)
class a_class
^~~~~~~
template_hell.cpp:27:7: note: candidate expects 1 argument, 0 provided
template_hell.cpp:27:7: note: candidate: constexpr a_class::a_class(a_class&&)
template_hell.cpp:27:7: note: candidate expects 1 argument, 0 provided
How do I pass the parameter to a_class through the template instance? Is this not possible?
You are passing a_class as a template parameter to the my_template template class and, since inside my_template class Stored_Data_Type m_data; is an object of following struct
struct Stored_Data_Type {
a_class data;
int set_count = 0;
};
m_data member is default constructed with
my_template() : m_data() {}
which tries to call a_class's default constructor which you haven't defined.
Therefore, you just need to define default constructor for your a_class class:
class a_class {
public:
a_class()
: m_data{0}
{}
// ...
};
Check live example
You could make use of Variadic arguments. See https://en.cppreference.com/w/cpp/language/parameter_pack. This allows us to forward on any arguments to the StoredDataType that are passed to our my_template class
Variadic templates allow us to write classes/methods that take an arbitrary number of arguments in a type-safe way and have all the argument handling logic resolved at compile-time, rather than run-time
#include <iostream>
template <typename T>
class my_template
{
public:
struct Stored_Data_Type
{
template<typename... TArgs>
Stored_Data_Type(TArgs&&... args) : data(std::forward<TArgs>(args)...)
{
}
T data;
int set_count = 0;
};
template<typename... TArgs>
my_template(TArgs&&... args) : m_data(std::forward<TArgs>(args)...) {}
T& write()
{
m_data.set_count++;
return m_data.data;
}
const T& get() const {return m_data.data;}
private:
Stored_Data_Type m_data;
};
class a_class
{
public:
a_class(int init): m_data(init) {};
void set(const int data) {m_data = data;};
const int get() const {return m_data;};
private:
int m_data;
};
class b_class : public a_class
{
public:
b_class(): a_class{0} {};
};
int main()
{
//a_class b(1);
b_class b;
b.set(2);
std::cout << "b: " << b.get() << std::endl;
my_template<int> my_int;
my_int.write() = 10;
std::cout << "my_int: " << my_int.get() << std::endl;
my_template<b_class> my_b;
my_b.write().set(2);
std::cout << "my_b: " << my_b.get().get() << std::endl;
// Compile error here:
my_template<a_class> my_a(1);
std::cout << "my_a: " << my_a.get().get() << std::endl;
}
I want to inherit copy constructor of the base class using using keyword:
#include <iostream>
struct A
{
A() = default;
A(const A &) { std::cerr << __PRETTY_FUNCTION__ << std::endl; }
A( A &&) { std::cerr << __PRETTY_FUNCTION__ << std::endl; }
A& operator=(const A &) { std::cerr << __PRETTY_FUNCTION__ << std::endl; return *this; }
A& operator=( A &&) { std::cerr << __PRETTY_FUNCTION__ << std::endl; return *this; }
};
struct B : A
{
using A::A;
using A::operator=;
B& operator=(const B &) { std::cerr << __PRETTY_FUNCTION__ << std::endl; return *this; }
B& operator=( B &&) { std::cerr << __PRETTY_FUNCTION__ << std::endl; return *this; }
};
int main()
{
A a;
B b;
b = a; // OK
B b1( a ); // compile error
B b2(std::move(a)); // compile error
return 0;
}
Inheriting assignment operator using using keyword works OK, but inheriting copy and move constructors causes a compilation error: an inherited constructor is not a candidate for initialization from an expression of the same or derived type.
http://coliru.stacked-crooked.com/a/fe84b429c391c894:
main.cpp:16:14: note: an inherited constructor is not a candidate for initialization from an expression of the same or derived type
main.cpp:8:5: note: candidate: A::A(A&&)
A( A &&) { std::cerr << __PRETTY_FUNCTION__ << std::endl; }
^
main.cpp:16:14: note: inherited here
using A::A;
Why can I inherit assignment operator but cannot inherit copy constructor? What is a difference? I could understand if I couldn't inherit assignment operators too. But inheriting assignment operators in contrary is considered OK. That is a little strange for me.
The story
What I want is similar to what is asked in this question: I want to just add new methods to existing class without modifying it (it's a class from another library).
http://coliru.stacked-crooked.com/a/149a6194717cd465:
#include <iostream>
struct A // not my class
{
};
struct B : A
{
using A::A;
using A::operator=;
void foo() { std::cerr << "fuu" << std::endl; }
};
A NotMyFunc()
{
return {};
}
int main()
{
B b(NotMyFunc());
b.foo();
return 0;
}
But I don't want to reimplement copy and move constructors.
You need a constructor for B that has A as parameter. Then you need to make the default constructor explicit.
struct B : A
{
using A::A;
using A::operator=;
B() = default;
B(const A& a) : A(a) {}
B(A &&a): A(std::move(a)) {}
};
I have a series of classes A, B, C, ...
I want to define conversion between any pair of classes.
The conversion has to follow the ordering of classes in the series.
For example, a conversions from A to C has the following steps:
A converts to B.
B finally converts to C
Similarly, a conversion from C to A has the following steps:
C to B
B to A
I try to modify the answer of my last question:
Chained conversion between classes without public inheritances
But I can only get chained conversion from C to A work.
Chained conversions in the opposite direction doesn't work.
How to make chained conversions in both directions work?
Test (also at godbolt.org)
#include <type_traits>
#include <iostream>
template <typename T1, typename T2>
struct conversion_chain {
template <typename T, typename = std::enable_if_t<
std::is_constructible_v<T, T2>
>>
operator T() {
return static_cast<T1 *>(this)->operator T2();
}
};
struct B;
struct C;
struct A { operator B(); };
struct B : conversion_chain<B, A>, conversion_chain<B, C> {
operator A();
operator C();
};
struct C : conversion_chain<C, B> {
operator B();
};
A::operator B() {
std::cout << "A -> B\n";
return B();
}
B::operator A() {
std::cout << "B -> A\n";
return A();
}
B::operator C() {
std::cout << "B -> C\n";
return C();
}
C::operator B() {
std::cout << "C -> B\n";
return B();
}
int main() {
A a = C();
C c = A(); // This line does not compile.
return 0;
}
Compilation error:
<source>:49:11: error: conversion from 'A' to non-scalar type 'C' requested
C c = A(); // This line does not compile.
^~~
Fix by making A inherits from conversion_chain:
struct A : conversion_chain<A, B> { operator B(); };
Assume I have a base class like this:
template<typename T>
class Base {
public:
Base& operator()(const T& value) {
this->value = value;
return *this;
}
T value;
};
Now I want to inherit from this class to create type-specific classes
class InheritedFloat : public Base<float> {} inheritedFloat;
Now here I try to catch this inheritance in a functon:
void function(const InheritedFloat& inherited) {
std::cout << inherited.value << '\n';
}
Calling this function like this works fine, of course:
int main() {
function(inheritedFloat); //(inheritedFloat is a global instance)
return 0;
}
But when I try to call it with the operator()(const float& value){...} member function, function(const InheritedFloat& inherited){...} doesn't see it as a InheritedFloat-Type but instead as a Base<float>-Type:
int main() {
function(inheritedFloat(10.f)); //error
return 0;
}
Error:
Error C2664 'void function(const InheritedFloat &)': cannot convert argument 1 from 'Base<float>' to 'const InheritedFloat &'
So how can I make operator()(const T& value){...} return InheritedFloat& instead of Base<float>&?
To clearify further, this is just a simplified example (of course). I have dozens of inheritance cases. So I can't just template-specify function()
template<typename T>
void function(const Base<T>& inherited) {
std::cout << inherited.value << '\n';
}
because each inheritance needs to be treated differently. Types will overlap, so there will be multiple Base<std::size_t> cases, for example.
The whole code:
#include <iostream>
template<typename T>
class Base {
public:
Base& operator()(const T& value) {
this->value = value;
return *this;
}
T value;
};
class InheritedFloat : public Base<float> {} inheritedFloat;
void function(const InheritedFloat& inherited) {
std::cout << inherited.value << '\n';
}
int main() {
function(inheritedFloat(10.f));
return 0;
}
Thanks for reading, I appreciate any help!
You can utilize CRTP here. By supplying extra template parameter you can make base class function return a reference to a derived class:
#include <iostream>
template<typename Derived, typename T>
class Base {
public:
Derived & operator()(const T& value) {
this->value = value;
return *static_cast<Derived *>(this);
}
T value;
};
class InheritedFloat : public Base<InheritedFloat, float> {} inheritedFloat;
void function(const InheritedFloat& inherited) {
std::cout << inherited.value << '\n';
}
int main() {
function(inheritedFloat(10.f));
return 0;
}
online compiler
The code below not compile in gcc 4.7, but compile in VS(9, 10, 11) also follows the gcc output.
#include <iostream>
using namespace std;
class A
{
public:
virtual void M() = 0;
};
class B
{
public:
inline B& operator<<(A &value)
{
value.M();
return *this;
}
};
class C: public A
{
public:
virtual void M()
{
cout << "Hello World" << endl;
}
};
int main()
{
B b;
C c;
b << c; //line not erro
b << C(); //Line with error
return 0;
}
gcc log
$g++ main.cpp -o test main.cpp: In function 'int main()':
main.cpp:36:12: error: no match for 'operator<<' in 'b << C()'
main.cpp:36:12: note: candidates are: main.cpp:14:15: note: B&
B::operator<<(A&) main.cpp:14:15: note: no known conversion for
argument 1 from 'C' to 'A&'
C++ does not allow you to bind a non-const reference to a temporary like you are attempting to do here:
b << C();
// ^^^^ temporary
VS allows you to do this as an "extension", but it is non-standard and therefore non-portable, as you have discovered.
What you need is a const reference in the relevant operator:
inline B& operator<<(const A& value)
// ^^^^^
One way of sorting it out is using C++11 rvalue binding feature.
B& operator<<(A&& value)
{ ... }
as an overload.