Re: Can you please help me explain constexpr?
On 2011-07-07 04:23, DeMarcus wrote:
On 2011-07-06 20:19, Daniel Kr?gler wrote:
Am 06.07.2011 01:03, schrieb DeMarcus:
[..]
Test case:
constexpr int i = MyClass(42).f();
Somehow I thought that the compiler would see that it's actually f()
that is used (which is constexpr) and that it uses a const int which
must have been initialized by the time it's used in f(). Therefore the
compiler could ignore that the constructor is non-constexpr. But I'm not
familiar with constexpr and I guess it's more complicated than that.
I don't understand your mental picture of constexpr as a function
specifier of non-static member functions, but the rationale for
providing literal types is to have a type such that objects of this type
can be used in /literal constants expressions/. This means that there
has to be at least one constructor that *could* be a constexpr
constructor (I'm using this conjunctive, because the language weakens
this for templates a bit: The reason for this is, that not every type
used to instantiate the template is required to be a literal type).
Without this constraint, constexpr does not make sense in this context.
In this example the sub expression MyClass(42) does not satisfy the
criteria for a constant expression, therefore MyClass can never be part
of a constant expression. Either
a) remove the constexpr from MyClass::f(), or
b) make MyClass::f() a constexpr *static* member function returning the
result of some constant expression (e.g. it could return the value of a
static member that can be used in constant expressions), or
c) provide another constexpr constructor for MyClass that initializes
the member i_.
I was just so keen on the following but I guess I can forget it.
class MyClass
{
public:
MyClass( int id, const std::string& name )
: id_(id), name_(name) { /* non-const ops */ }
// Conversion operator to be used in switch.
constexpr operator int() const { return id_; }
std::string getName() const { return name_; }
private:
const int id_;
const std::string name_;
};
Indeed, I'm not seeing any considerable difference compared to your
original example, because the shown MyClass constructor still could
never be used as part of a constant expression.
const MyClass FLYING_CAR( 42, "Flying car" );
const MyClass DRINKIN_BOAT( 4711, "Drinking boat" );
The objects FLYING_CAR and DRINKIN_BOAT are constant, but they still
cannot be used within any /literal constant expression/, because they
have not been constructed within an initialization that itself would be
considered as compatible to such expressions. For such expressions,
every sub-part must be a literal constant expression as well.
In C++03 the object specifier 'const' has a dual nature:
1) It means that the corresponding object cannot be changed.
2) It *can* mean that the corresponding object can be used in other
constant expressions.
In C++0x we have a simple tool to ensure that bullet (2) is satisfied as
well: Just use constexpr as part of an object definition. If you would
change your constant variable definitions to
constexpr MyClass FLYING_CAR( 42, "Flying car" );
constexpr MyClass DRINKIN_BOAT( 4711, "Drinking boat" );
you should observe that these definitions are *not* well-formed, because
these objects are constant but *not* usable as part of a literal
constant expression.
If you want to use your MyClass objects within constant expressions you
need to extract the std::string member and you need to ensure that there
is a constexpr constructor, e.g.
class MyClass
{
public:
constexpr MyClass(int id) : id_(id) {}
constexpr operator int() const { return id_; }
private:
const int id_;
};
You don't need to declare the id_ member as const as you do above, but
you can.
If you need a mapping of MyClass constants to strings, you can still do
that as you would have done this with enumerations or other C++03
constant-expression compatible values (Just define a separate map that
relates MyClass objects to string values).
If you really want to combine a name representation and the integer
value in a single object, you can realize this as follows:
#include <iostream>
#include <string>
class MyClass
{
public:
constexpr MyClass(int id, const char* name) :
id_(id), name_(name)
{}
constexpr operator int() const { return id_; }
std::string getName() const { return name_; }
private:
const int id_;
const char* name_;
};
constexpr auto FLYING_CAR = MyClass(42, "Flying car");
constexpr auto DRINKING_BOAT = MyClass(4711, "Drinking boat");
int randomVehicle();
int main()
{
int i = randomVehicle();
switch( i )
{
case FLYING_CAR:
std::cout << "Lycky you!" << std::endl;
break;
case DRINKING_BOAT:
std::cout << "Bon voyage!" << std::endl;
break;
default:
std::cout << "Mi casa es tu casa!" << std::endl;
}
}
This works, provided that the second argument of the MyClass constructor
is usable in an /address-constant/ expression, which is true for string
literals.
HTH & Greetings from Bremen,
Daniel Kr?gler
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]