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; }
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
There is a great FAQ(isocpp) just demonstrates why we potentaily cannot we use static object instead of static pointer to avoid Fiasco problem. I came across a sentence in the answer that gave me a headache
.... By changing the declaration from static X* xp = new X();
to static X xp;, we still correctly handle the initialization
situation but we no longer handle the deinitialization situation. For
example, if there are 3 static objects, say a, b and c, that use ans
during their destructors, the only way to avoid a static
deinitialization disaster is if xp is destructed after all three.
main.cpp
#include "x.h" // struct X { X(int); void f(); };
X& x(int i)
{
static X xp{ i };
return xp;
}
struct Y
{
Y(int);
private: int m_y;
};
Y::Y(int i) {
x(i).f();
}
other.cpp
Y y{ 20 };
struct X {
X(int);
void f();
private: int m_x;
};
X::X(int j): m_x(j) {};
void X::f() { std::cout << m_x << std::endl; }
Then they continue with:
The point is simple: if there are any other static objects whose
destructors might use xp after xp is destructed, bang, you’re dead.
I just cannot understand how this problem can happen. I need a practical example that demonstrates how this problem can occur.
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 )
{}
};
I want to explicitly change the second parameter in a constructor of a struct, in the following scenario. Is it possible, if so, how?
struct foo{
int x;
int y;
foo(int a=4, int b=6){
x=a;
y=b;
}
};
int main(){
foo *f = new foo();
cout<<f->x<<" "<<f->y<<endl;
//4 6
foo *g = new foo(3,4);
cout<<g->x<<" "<<g->y<<endl;
//3 4
foo *h = new foo(3);
cout<<h->x<<" "<<h->y<<endl;
//3 6
//Can something like this be
//done in C++, if I want
//to change the value of the
//second variable only
foo *k = new foo(b = 13);
return 0;
}
Is it possible, if so, how?
It is not possible with constructor. In general, c++ does not support named keyword arguments to functions, and it is not possible to skip arguments even if they have a default, if you want to pass a non-default after it.
It will be possible without constructor using list initialisation syntax since C++20 using designated initialisers, if you use default member initialisers:
struct foo{
int x = 4;
int y = 6;
};
int main(){
foo f {.y = 4};
}
You can achieve something similar with tag dispatching; No need for future standard:
struct foo{
int x = 4;
int y = 6;
enum Xtag { Xinit };
enum Ytag { Yinit };
foo(int a, int b) : x(a), y(b) {}
foo(Xtag, int a) : x(a) {}
foo(Ytag, int b) : y(b) {}
};
int main(){
foo f(foo::Yinit, 4);
}
A solution using lambda that can be used without modifying an existing class. Following works with your definition of foo:
auto make_foo_x4 = [](int b) {
return foo(4, b);
};
foo f = make_foo_y(4);
The downside is that we have to explicitly repeat the default value of x, so this can break assumptions if the default is changed in class definition.
struct Foo
{
constexpr static int n = 10;
};
void f(const int &x) {}
int main()
{
Foo foo;
f(Foo::n);
return 0;
}
I get error: main.cpp|11|undefined reference to `Foo::n'|. Why?
The compiler error is required by the standard. Since your function
void f(const int& x)
takes it argument by reference, in the call
f(Foo::n);
the variable Foo::n is odr-used. Hence a definition is required.
There are 2 solutions.
1 Define Foo::n:
struct Foo
{
constexpr static int n = 10; // only a declaration
};
constexpr int Foo::n; // definition
2 Take the argument of f by value:
void f(int x);
What compiler version do you use? It seems, it has some issues with C++11 dialect (ideone.com compiled that with 100% success).
Maybe, it's a good idea to try this instead?
struct Foo
{
static int n;
};
int Foo::n = 10;