Prevent initialization with parentheses or braces - c++

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

Related

Why I can not use designated initalizers with structs that are not aggregates?

C++ has a nice new feature:
struct Point{
int x;
int y;
int z;
};
Point p{.x=47, .y=1701, .z=0};
But if I add a constructor then I am forbidden from using the nice designated initalizers syntax:
struct Point{
Point(int x, int y, int z = 0): x(x), y(y), z(z){}
int x;
int y;
int z;
};
static Point p{.x=47, .y=1701, .z = 0};
error: designated initializers cannot be used with a non-aggregate
type 'Point'
Am I missing something obvious(why it would be terrible if designated initalizers worked with structs/classes that have public members, but are not aggregates) or this is just a missing feature that is just not added to the standard?
Aggregate initialization (including initialization with designed initializers) circumvents the constructor of the class.
This is not a problem for aggregates, since they aren't allowed to have user-defined constructors. But if you allow this kind of initialization for classes with user-provided constructors (that do something useful), it can be harmful.
Consider this example:
class A
{
static std::map<A *, int> &Indices()
{
static std::map<A *, int> ret;
return ret;
}
public:
int dummy = 0;
A(int index)
{
Indices().emplace(this, index);
}
A(const A &) = delete;
A &operator=(const A &) = delete;
~A()
{
auto it = Indices().find(this);
std::cout << "Deleting #" << it->second << '\n';
Indices().erase(it);
}
};
If you were able to do A{.dummy = 42};, you'd get UB in the destructor, and would have no way to protect against this kind of usage.
Designated initalizers where a feature lifted from C. Most C++ compilers are also C compilers, and it was a C feature first.
They added a restriction (that the initializers be in order) and applied it to C++ types that matched C types, and got it into C++. Most major C++ compilers already had it as a C++ extension (without the restriction); the restriction was checked with the compiler implementors as being reasonable, and then the "cost" of adding the feature was really low.
Once you have a constructor, it becomes a larger language issue. Does the initializer refer to the constructor arguments? If yes, we run into the problem that argument names are not unique. If no, then how do we handle it when the constructor sets a value and the initializer set a different value?
Basically we need function-arguments-by-name to get sensible designated initializers with constructors. And that is a new feature, not one simply lifted from C.
The workaround (for named arguments) is:
struct RawPoint{
int x = 0;
int y = 0;
int z = 0;
};
struct Point {
Point( int x_, int y_, int z_ = 0 ):
x(x_), y(y_), z(z_)
{}
explicit Point( RawPoint pt ):
Point( pt.x, pt.y, pt.z )
{}
int x, y, z;
};
then you can do:
Point pt( {.x=3} );
by accessing the RawPoint's designated initializer feature.
This is the same way you can have designated initializers in function calls.
This also works:
struct Point:RawPoint {
Point( int x, int y, int z = 0 ):
RawPoint{x,y,z}
{}
explicit Point( RawPoint pt ):
RawPoint( pt )
{}
};

What happens when std::make_unique<T>() assigns to std::unique_ptr<T>?

I have one doubt in std::unique_ptr.
When we assign std::make_unique() having no arguments, What will happen?
For example,
struct A {
int a, b;
A() {}
A(int w, int e) : a(w), b(e) {}
};
int main() {
A h(1, 2);
std::unique_ptr<A> hello = std::make_unique<A>();
std::cout << hello->a << std::endl;
}
In the above code, I mentioned default constructor, I got output for hello->a as a garbage value(random negative value)
But,
when I change the struct as below,
struct A {
int a, b;
A() {a=0;b=0;}
A(int w, int e) : a(w), b(e) {}
};
The result value of hello->a as 0.
Why default constructor not assign the int as 0 when using std::make_unique()?
The arguments passed to std::make_unique<A>() are the arguments passed to the corresponding constructor of A. Here you're not providing any, hence the default constructor of A will be called.
Why default constructor not assign the int as 0 when using std::make_unique()?
Members of builtin type that are not initialized are left with an indeterminate value. This behavior is not related to either std::unique_ptr or std::make_unique; it is just how builtin types are default initialized.
Initialize them:
struct A {
int a, b;
A(): a(0), b(0) {}
A(int w, int e) : a(w), b(e) {}
};

Why can't non-static data members be constexpr?

This is valid code:
struct S {
constexpr S(int x, int y): xVal(x), yVal(y) {}
constexpr S(int x): xVal(x) {}
constexpr S() {}
const int xVal { 0 };
const int yVal { 0 };
};
But here I'd really like to declare xVal and yVal constexpr--like this:
struct S {
constexpr S(int x, int y): xVal(x), yVal(y) {}
constexpr S(int x): xVal(x) {}
constexpr S() {}
constexpr int xVal { 0 }; // error!
constexpr int yVal { 0 }; // error!
};
As indicated, the code won't compile. The reason is that (per 7.1.5/1), only static data members may be declared constexpr. But why?
Think about what constexpr means. It means that I can resolve this value at compile time.
Thus, a member variable of a class cannot itself be a constexpr...the instance that xVal belongs to does not exist until instantiation time! The thing that owns xVal could be constexp, and that would make xVal a constexpr, but xVal could never be constexpr on its own.
That does not mean that these values can't be const expression...in fact, a constexpr instance of the class can use the variables as const expressions:
struct S {
constexpr S(int x, int y): xVal(x), yVal(y) {}
constexpr S(int x): xVal(x) {}
constexpr S() {}
int xVal { 0 };
int yVal { 0 };
};
constexpr S s;
template <int f>//requires a constexpr
int foo() {return f;}
int main()
{
cout << "Hello World" << foo<s.xVal>( )<< endl;
return 0;
}
Edit: So there has been alot of discussion below that reviewed that there was a couple of implied questions here.
"why can't I enforce all instances of a class to be constexpr by declaring its members to be constexpr?"
Take the following example:
//a.h
struct S;
struct A {std::unique_ptr<S> x; void Foo(); A();/*assume A() tries to instantiate an x*/}
//main.cpp
int main(int argc, char** argv) {
A a;
a->foo();
}
//S.h
struct S {
constexpr S(int x, int y): xVal(x), yVal(y) {}
constexpr S(int x): xVal(x) {}
constexpr S() {}
constexpr int xVal { 0 }; // error!
constexpr int yVal { 0 };
};
The definition of A and S could be in completely different compilation units, so the fact that S must be constexpr may not be known until link time, especially if the implementation of A is forgotten. Such ambiguous cases would be hard to debug and hard to implement. Whats worse is that the interface for S could be exposed entirely in a shared library, COM interface, ect...This could entirely change all the infrastructures for a shared library and that would probably be unacceptable.
Another reason would be how infectious that is. If any of the members of a class were constexpr, all the members (and all their members) and all instances would have to be constexpr. Take the following scenario:
//S.h
struct S {
constexpr S(int x, int y): xVal(x), yVal(y) {}
constexpr S(int x): xVal(x) {}
constexpr S() {}
constexpr int xVal { 0 }; // error!
int yVal { 0 };
};
Any instance of S would have to be constexpr to be able to hold an exclusively constexpr xval. yVal inherently becomes constexpr because xVal is. There is no technical compiler reason you can't do that (i don't think) but it does not feel very C++-like.
"OK, but i REEAAALLLY want to make all instances of a class constexpr. What is the technical limitation that prevents me from doing that".
Probably nothing other than the standards committee didn't think it was a good idea. Personally, I find it having very little utility...I don't really want to define how people use my class, just define how my class behaves when they use it. When they use it, they can declare specific instances as constexpr (as above). If I have some block of code that I would like a constexpr instance over, I'd do it with a template:
template <S s>
function int bar(){return s.xVal;}
int main()
{
cout << "Hello World" << foo<bar<s>()>( )<< endl;
return 0;
}
Though I think you'd be better off with a constexpr function that could be used both in the restrictive an non restrictive ways?
constexpr int bar(S s) { return s.xVal; }

std::array constructor inheritance

I'm trying to get extended variant of std::array for math vectors (and expose same interface as array does without boilerplate code). I know about std::valarray but I want fixed size for proper typing in matrix multiplications. Thus I array fits perfectly. But when I try to inherit constructor it fails.
struct vec2d : std::array<float, 2>
{ using array::array; }; // simplified
struct vec : std::vector<float>
{ using vector::vector; };
std::array<float, 2> x = {1, 2};
vec y = {1, 2};
vec2d z = {1, 2}; // error: could not convert ‘{1, 2}’
// from ‘<brace-enclosed initializer list>’ to ‘vec2d’
This error reported for GCC 4.8.2 and for clang 3.4. Last says that vec2d have only implicit default/copy/move constructors. Yes, array have only implicit ctor in contrary to vector which have ctor from initializer_list. But since ctors are inherited it is natural to inherit possibility to initialize it in a same way as array initialized.
Question: Why we have that error instead of expected behavior (similar to array initialization)?
Note: I that I can write forwarding manually to make it work, but this doesn't look as elegant as ctor inheritance.
struct vec2d : std::array<float, 2>
{
using array::array;
// nasty boilerplate code I don't want to have in C++11
template <typename... Args>
vec2d(Args &&... args) : array({float(std::forward<Args>(args))...}) {}
};
std::array is designed to be an aggregate, so it intentionally does not define any constructors.
Unfortunately, this means it's not possible to inherit from it and get the same behaviour, as aggregates cannot have base classes.
Why do you need to inherit from std::array anyway? Do you plan to add any private members? If not, then you could just build your framework around free functions operating on std::array, or perhaps a typedef to it.
If you really want to inherit from std::array, you'll have to accept losing the aggregate status and provide any constructors you want yourself.
Note that the answer above applies to C++11 and C++14 only. In C++17, the definition of aggregates was loosened to allow public base classes in them, so simply deriving from std::array and removing the using declaration is enought to make the code compile:
struct vec2d : std::array<float, 2>
{ }; // simplified
std::array<float, 2> x = {1, 2};
vec2d z = {1, 2};
[Live example]
I had exactly the same problem, trying to mimic the behavior of numpy.
The way I solved this was to implement a constructor that takes as argument an std::array<float,N> (with & or && or without anything depending on the needs).
The initializer list is then cast automatically to that type, and the right constructor is then called. To be more concrete:
#include <array>
#include <ostream>
#include <iostream>
using namespace std;
template <int N> class Row: public array<double,N>{
public:
Row<N>(){}
// this is the constructor needed
Row<N>(array<double,N> a) : array<double,N> (a) {}
// or, alternatively,
// Row<N>(array<double,N>&& a) : array<double,N> (a) {}
Row<N>(array<double,N>& a) : array<double,N> (a) {}
// other things that may be relevant
Row<N> operator+(Row<N>& other){
Row<N> result;
for(int i =0; i < N ; ++i){
result[i] = (*this)[i] + other[i]; // notice '*'
}
return result;
}
// for lvalues
template <int n> friend ostream& operator<<(ostream& os, Row<n>& r);
// for rvalues
template <int n> friend ostream& operator<<(ostream& os,Row<n>&& r);
};
// for lvalues
template <int N> ostream& operator<<(ostream& os, Row<N>& r){
for(int i =0; i < N ; ++i) os << r[i] << "\t";
return os;
}
// for rvalues
template <int N> ostream& operator<<(ostream& os, Row<N>&& r){
for(int i =0; i < N ; ++i) os << r[i] << "\t";
return os;
}
int main(){
// here Row(array<double,3>&&) is called
// or Row(array<double,3>)
Row<3> a{{1,2,3}}; // same as Row<3> a({1,2,3});
array<double,3> arr = {1,2,3};
Row<3> b(arr);
cout << a << endl; // a and b are lvalues
cout << b << endl;
cout << (a+b) << endl; // (a+b) is a rvalue
return 0;
}

C++ constructor initialization list invokes default constructor. Why?

take a look at my code. It will convert Vector4 to Vector4.
There is this copy constructor, which automatically does the conversion component by component. I can't understand one thing: why at the end of construction, when each component is correctly set up, default constructor is invoked on each component making output vector empty. You can see execution flow in the output below. Interesting is, that if I replace initialization list by 4 assignments, the code works as expected.
Compiler is VS2013.
#include <cstdio>
using namespace std;
struct half
{
unsigned short data;
half() : data(0) { printf("half::default_constructor\n"); }
half(half& pattern) : data(pattern.data) { printf("half::copy_constructor\n"); }
explicit half(float pattern) : data(16) { printf("half::from_float_constructor\n"); }
operator float() { printf("half::to_float_operator\n"); return 3.0f; }
};
template <typename T>
struct Vector4
{
Vector4() : x(0), y(0), z(0), w(0) { }
Vector4(T value) : x(value), y(value), z(value), w(value) { }
Vector4(T x, T y, T z, T w) : x(x), y(y), z(z), w(w) { }
template <typename U>
Vector4(Vector4<U>& other) : x((T)other.x), y((T)other.y), z((T)other.z), w((T)other.w) { }
union
{
struct { T x, y, z, w; };
struct { T r, g, b, a; };
};
};
int main()
{
Vector4<float> a(0, 1, 4, 6);
Vector4<half> b(a);
}
Output from this program:
half::from_float_constructor
half::to_float_operator
half::from_float_constructor
half::from_float_constructor
half::to_float_operator
half::from_float_constructor
half::from_float_constructor
half::to_float_operator
half::from_float_constructor
half::from_float_constructor
half::to_float_operator
half::from_float_constructor
half::default_constructor
half::default_constructor
half::default_constructor
half::default_constructor
The reason is that the code is invalid. You cannot store non-POD types in a union. Your code results in undefined behaviour. I don’t know exactly what the compiler does as a result, and why it invokes the default constructor1 – but that’s undefined behaviour for you.
1 Although I have a theory: it probably attempts to initialise r, g, b and a.
First of all C++ has no anonymous structures. So this definition of Vector4 member
union
{
struct { T x, y, z, w; };
struct { T r, g, b, a; };
};
is not C++ compliant. I think you use MS VC++ that has such language extension.
Now let consider what occurs.
In mem-initializer list of the template constructor
template <typename U>
Vector4(Vector4<U>& other) : x((T)other.x), y((T)other.y), z((T)other.z), w((T)other.w) { }
C-style casting as for example (T)other.x invokes constructor of class half
explicit half(float pattern) : data(16) { printf("half::from_float_constructor\n");
The result of this call is creating a temporary object of type half
You may not apply the copy constructor of class half because its parameter is declared as non-const reference and temporary objects may not bind to non-const references.
half(half& pattern) : data(pattern.data) { printf("half::copy_constructor\n"); }
So the constructor searches other paths to do the task.
It can convert the temporary object to an object of type float
operator float() { printf("half::to_float_operator\n"); return 3.0f; }
};
that to call at last constructor
explicit half(float pattern) : data(16) { printf("half::from_float_constructor\n"); }
Thus you get the following sequence of messages
half::from_float_constructor
half::to_float_operator
half::from_float_constructor
I prepared a more simple C++ compliant example that demonstrates the same behaviour
#include <iostream>
struct A
{
float x = 0.0f;
};
struct B
{
explicit B( float ){ std::cout << "B::from_float_constructor" << std::endl; }
B( B & ){ std::cout << "B::from_copy_constructor" << std::endl; }
operator float () const
{
std::cout << "B::to_float_operator" << std::endl;
return 0.0f;
}
};
struct C
{
B b;
C( A a ) : b( ( B )a.x ) {}
};
int main()
{
A a;
C c( a );
return 0;
}
The output is
B::from_float_constructor
B::to_float_operator
B::from_float_constructor