I'm trying to write a template class that doesn't have default constructor.
For A<int> works fine, but for A<A<int>> I don't know how to get it to work.
1 #include <iostream>
2 using namespace std;
3
4 template <typename T>
5 class A {
6 T x;
7
8 public:
9 A(T y) { x = y; }
10 };
11
12 int main() {
13 A<int> a(0);
14 A<A<int> > b(A<int>(0));
15
16 return 0;
17 }
Error list from clang
test.cpp:9:5: error: constructor for 'A<A<int> >' must explicitly initialize the member 'x' which does not have a default constructor
A(T y) { x = y; }
^
test.cpp:14:16: note: in instantiation of member function 'A<A<int> >::A' requested here
A<A<int> > b(A<int>(0));
^
test.cpp:6:7: note: member is declared here
T x;
^
test.cpp:5:9: note: 'A<int>' declared here
class A {
^
You are not properly constructing x in the initializer list of the constructor, hence A(T y) must default construct x before invoking operator= to copy assign y to it.
int provides a default constructor, which just let the value uninitialized, but A<int> does not.
Your constructor should be
A(T y) : x(y) { }
Related
Given a class C, is it possible to prevent construction using parenthesis (e.g. forbid C c(1);) or braces (e.g. forbid C c{1};)?
I am interested in the question per se, but since it got asked what is my motivation:
I do not like the following behaviours:
#include <array>
struct aggregate{ int x,y; };
int main() {
aggregate A{1};
std::array< aggregate, 2 > B{1,2,3};
}
which yields
A.x=1, A.y=0
B[0].x=1, B[0].y=2,B[1].x=3, B[1].y=0
The first one is at least kind of reasonable, the second one not at all. I rather would like to have a compile time error for both of them.
My goal is to write a container array similar to (and as efficient as) std::array which prevents such bad constructions.
To be constructed like:
#include <array>
struct aggregate{ int x,y; };
int main() {
array< aggregate, 2 > A; // all elements default initialized
array< aggregate, 2 > B{aggregate{1,2},aggregate{3,4}};
array< aggregate, 2 > C{{1,2},{3,4}}; // would be nice but not mandatory
// everything below here should yield compile time errors
array< aggregate, 2 > D(1,2);
array< aggregate, 2 > D{1,2};
array< aggregate, 2 > D({1,2});
array< aggregate, 2 > D(1,2,3,4);
array< aggregate, 2 > D{1,2,3,4};
// etc...
}
To further detalize my motivation (since it seems, my question is not going to be answered): I am not so concerned about the class aggregate given above, but about the class array. Thus, it is not an option to remove/add ctors from the class.
So far I have the following code:
It prevents nearly all of my unwanted initializations, except that it calls unnecessary often a ctor. So my idea is: In Debug mode I typedef array to the class tmm::array`` in the code below, in Release mode I typedef it to std::array. But the problem arises that tmm::arrayallows constructs which are not allowed forstd::array`.
#include <array>
#include <iostream>
#include <type_traits>
template <typename T, typename ...Ts>
inline constexpr bool areT_v = std::conjunction_v<std::is_same<T,Ts>...>;
struct CLASS {
int x, y;
CLASS() noexcept {
std::cout << "default constructed" << std::endl;
}
CLASS(int x_,int y_) noexcept : x(x_), y(y_) {
std::cout << "constructed" << std::endl;
}
CLASS( CLASS const & other ) noexcept : x(other.x), y(other.y) {
std::cout << "copy constructed" << std::endl;
}
};
namespace tmm {
template< typename T, int rows >
struct array
{
template< typename ... L > constexpr inline
array( L && ... lst ) noexcept : content{ std::forward< L >( lst ) ... } {
static_assert( sizeof...(L) == rows || sizeof...(L)==0 );
static_assert( areT_v<T,L...>, "" );
}
array() = default; // not sure whether I need this one
T content[rows];
};
}
int main() {
#define INIT CLASS{1,2},CLASS{3,4},CLASS{5,6}
std::array<CLASS,3>tb3b {INIT}; // 3 ctors
tmm::array<CLASS,3> sb3b {INIT}; // 3 ctors and 3 copy ctors
// std::array<CLASS,3> sp3a (INIT); // does not compile
tmm::array<CLASS,3>tp3a (INIT); // compiles
}
Given a class C, is it possible to prevent construction using parenthesis (e.g. forbid C c(1);) or braces (e.g. forbid C c{1};)?
To answer your question directly, the object of class C will only be constructible with the constructors you provide. That is, if C does not have a constructor from a single int, it won't be constructible using either C c(1); or C c{1}; syntax.
There are a few caveats, especially given your later explanation. First, if your class C is an aggregate then it follows the standard language rules for initialization (that is, it allows aggregate initialization). You cannot have an aggregate class and prohibit its aggregate initialization at the same time. The only way around it is to specify the constructors you want to allow, at which point the class is no longer an aggregate:
struct C
{
int x,y;
C() : x(0), y(0) {}
C(int x, int y) : x(x), y(y) {}
};
C a(10); // error - no constructor from a single int
Next, type conversions may still happen as part of initialization. If you also want to prohibit those, you may use deleted constructors or SFINAE. For example:
struct C_no_float
{
int x,y;
C_no_float() : x(0), y(0) {}
C_no_float(int x, int y) : x(x), y(y) {}
C_no_float(double x, double y) = delete;
};
C_no_float a(10, 20); // ok
C_no_float b(1.0, 2.0); // error - calling a deleted constructor
Note that using deleted functions may still be error-prone since the constructor from ints may still be called as a result of type conversion. In other words, this approach does not guarantee that C_no_float is not constructible from anything but exactly a pair of ints. Using SFINAE allows for stronger restriction on argument types:
struct C_only_int
{
int x,y;
C_only_int() : x(0), y(0) {}
template< typename T, typename = std::enable_if_t<std::is_same_v<T, int>> >
C_only_int(T x, T y) : x(x), y(y) {}
};
C_only_int a(10, 20); // ok, T is int
C_only_int b(10l, 20l); // error - T would be long,
// template argument substitution fails
class Q
{
Q(const Q &obj) {} // copy constructor
Q& operator= (const Q& a){} // equal op overload
}
template <class T>
class B{
public : T x;
B<T>(T t) {
// x = t; }
}
int main()
{
Q a(2);
a.init(1,0);
a.init(2,1);
B <Q> aa(a); // this line gives error
}
How to initialize template class with copy constructor? B aa(a); // this line gives error
I want to solve it but I could not.
Error:
no matching function for call to 'Q::Q()'|
candidate: Q::Q(const Q&)|
candidate expects 1 argument, 0 provided|
candidate: Q::Q(int)|
candidate expects 1 argument, 0 provided|
To solve the mentioned error just add a default constructor inside class Q as shown below
class Q
{
Q() //default constructor
{
//some code here if needed
}
//other members as before
};
The default constructor is needed because when your write :
B <Q> aa(a);
then the template paramter T is deduced to be of type Q and since you have
T x;
inside the class template B, it tries to use the default constructor of type T which is nothing but Q in this case , so this is why you need default constructor for Q.
Second note that your copy constructor should have a return statement which it currently do not have.
I have come up with a problem that I am unable to explain in light of my knowledge of C++.
I have a class that takes a constant reference to a struct of type X but when I pass an argument of type X (which i previously initialized) I get an error from the compiler saying it can't convert to the first member of a member of X. This doesn't make any sense to me.
The error is similar in clang and g++ which leads me to think I am missing something egregious.
error: no viable conversion from 'const Foo::X' to 'Foo::FooTypes'
Why is it trying to convert from X to FooTypes which is the first member of Z?
class Foo {
public:
enum FooTypes {
JPEG
};
struct Z {
FooTypes type;
int a;
};
struct X {
Z caps;
double host_address;
};
Foo(const X& x);
private:
const X x;
};
Foo::Foo(const Foo::X& x) :
x{x} {
}
int main() {
Foo::X new_x = {
{Foo::JPEG, 1}, 1.58 };
Foo *s = new Foo(new_x);
delete s;
return 0;
}
You should use round brackets instead of curly brackets:
...
const X m_x;
};
Foo::Foo(const Foo::X& x) :
m_x(x) {
}
...
here is list initialization:
...
m_x{x.caps, x.host_address}
...
Edit
#PauloNeves
I just found Bjarne Stroustrup's document http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2640.pdf, which contains next definition:
The general idea of "initializer lists" (as discussed for many years
in EWG) is to allow the use of a brace-enclosed list of expressions in
all contexts that allow initializers. The following list is lifted
from N2532:
Variable initialization; e.g., X x {v};
Initialization of a temporary; e.g, X{v}
Explicit type conversion; e.g. x X{v};
Free store allocation; e.g. p new X{v}
Return value; e.g., X f(){ /* ... */ return {v}; }
Argument passing; e.g., void f(X); /* ... */ f({v});
Base initialization; e.g., Y::Y(v) : X{v} { /* ... */ };
Member initialization; e.g., Y::Y(v) : mx{v} { X mx; /* ... */ };
I think that described Member initialization is your case. So as for me it looks like g++4.9 defect.
Please can someone help explain why I get an error when compiling the following code using Xcode 5.1 on OS X. Apple LLVM version 5.1 (clang-503.0.40) (based on LLVM 3.4svn).
I want construct X below, passing it a vector of pairs.
#include <iostream>
#include <string>
#include <vector>
#include <utility>
struct X
{
public:
typedef std::vector<std::pair<std::string, std::string>> VectorType;
X(VectorType& params) : m_params(params)
{
}
VectorType m_params;
};
int main(int argc, const char * argv[])
{
X::VectorType pairs
{
{ "param-1", "some-string-1"}, // pair 0
{ "param-2", "some-string-2"}, // pair 1
{ "param-3", "some-string-3"}, // pair 2
{ "param-4", "some-string-4"}, // pair 3
{ "param-5", "some-string-5"}, // pair 4
{ "param-6", "some-string-6"}, // pair 5
{ "param-7", "some-string-7"} // pair 6
};
X x
{
{pairs[0], pairs[2], pairs[5]}
};
return 0;
}
The error reported is:
/main.cpp:37:7: error: no matching constructor for initialization of 'X'
X x
^
/main.cpp:6:8: note: candidate constructor (the implicit move constructor) not viable: cannot convert initializer list argument to 'X'
struct X
^
/main.cpp:6:8: note: candidate constructor (the implicit copy constructor) not viable: cannot convert initializer list argument to 'const X'
struct X
^
/main.cpp:11:5: note: candidate constructor not viable: cannot convert initializer list argument to 'VectorType &' (aka 'vector<std::pair<std::string, std::string> > &')
X(VectorType& params) : m_params(params)
^
1 error generated.
Your constructor should take its argument by const reference
X(VectorType const & params)
^^^^^
Otherwise, you can't pass a temporary vector (as you try to do), since temporaries can't bind to non-const lvalue references.
X has 3 constructors:
Your user-defined one, which suppresses the automatic default-ctor:
X(VectorType& params)
The automatic copy-ctor and move-ctor:
X(X&&) noexcept
X(const X&)
The custom one expects an lvalue, never an xvalue or a constant object.
You probably want to split the ctor thus:
X(const VectorType& params) : m_params(params) {}
X(VectorType&& params) : m_params(std::move(params)) {}
How do I call a template constructor whith objects of another class?
Example:
#include <iostream>
using namespace std;
class RGB{
private:
int _R;
int _G;
int _B;
public:
RGB(int a, int b, int c){
_R=a;
_G=b;
_B=c;
}
};
class Gray{
int _G;
public:
Gray(int x){
_G=x;
}
};
template <class T1, class T2>
class Pixel{
public:
Pixel(T1 i, T2 j, int a, int b){
foo=i;
bar=j;
x=a;
y=b;
}
void setPixel(T1 a, T2 b){
foo=a;
bar=b;
}
void getPixel();
private:
int x;
int y;
T1 foo;
T2 bar;
};
int main(){
Gray g(3);
RGB rgb(4, 5, 6);
Pixel<Gray,RGB> pi(g, rgb, 10,27);
return 0;
}
I can use the constructor of the class only with the default constructors of the two classes Bar1 and Bar2 but not with the two objects of the classes that I have created before(in this case bar1 and bar2).
Errors are:
main.cpp: In instantiation of 'Pixel::Pixel(T1, T2, int, int) [with T1 = Gray; T2 = RGB]':
main.cpp:62:37: required from here
main.cpp:35:37: error: no matching function for call to 'Gray::Gray()'
main.cpp:35:37: note: candidates are:
main.cpp:26:5: note: Gray::Gray(int)
main.cpp:26:5: note: candidate expects 1 argument, 0 provided
main.cpp:23:7: note: Gray::Gray(const Gray&)
main.cpp:23:7: note: candidate expects 1 argument, 0 provided
main.cpp:35:37: error: no matching function for call to 'RGB::RGB()'
main.cpp:35:37: note: candidates are:
main.cpp:16:9: note: RGB::RGB(int, int, int)
main.cpp:16:9: note: candidate expects 3 arguments, 0 provided
main.cpp:9:7: note: RGB::RGB(const RGB&)
main.cpp:9:7: note: candidate expects 1 argument, 0 provided
The errors go away if I add default values to the RGB and Gray constructors.
Sorry for the mess, I'm new here...
You need to learn about initialization lists:
Pixel(T1 i, T2 j, int a, int b) : x(a), y(b), foo(i), bar(j) {
}
foo=i;, etc. in the body of the Pixel constructor is assigning to an already existing object. When your Pixel constructor is called, it trys to call the default constructor for its subobjects, Gray and RGB, however, both those types do not have default constructors generated since you provide a user-defined constructor. The solution is to use initialization lists to initialize the subobjects in your Pixel class.
Also, note that subobjects of the class are constructed in the order they are declared inside the class definition, so you should always initialize them in the same order as they are declared in your constructor initialization list.