rvalue qualified method and const expression - c++

How can I make a const rvalue in a natural way?
Here is a simple example :
struct A {
void f() &&{} // 1
constexpr bool f() const &{return true;} // 2
constexpr bool f() const &&{return true;} // 3 really usefull?
};
int main() {
static_assert(A{}.f()); // does not compile because call 1 instead of 3
constexpr A a;
static_assert(a.f());
}
Why does the first static_assert() call 1 instead of 3?

The issue here is A{} doesn't give you a const A, it just gives you an A so 2 gets called since it is callable on a non const rvalue.
If you want 4 to be called you need to make a const A and you can do that using an alias declaration. If you have using A_const = const A; then A_const{} gives you a const A and A_const{}.f() will call 4 instead of 2.
Essentially what it does is static_assert(const A{}.f());, but since syntactically you can't write it that way we need to using declaration to give us a single word type that is a const A.
Additionally you could rewrite
static_assert(A{}.f());
as
static_assert(std::add_const_t<A>{}.f());
and also get a const A rvalue.

The question is, how can I make rvalue object works on a const expression
Another way can be through a constexpr function returning an A const
#include <iostream>
struct A
{
std::size_t f() & { return 1u; }
std::size_t f() && { return 2u; }
constexpr std::size_t f() const & {return 3u; }
constexpr std::size_t f() const && {return 4u; }
};
constexpr A const foo ()
{ return {}; }
int main()
{
static_assert( foo().f() == 4u, "!" );
}

Maybe this does what you want?
struct A {
constexpr bool f() &&{return true;} // 1
constexpr bool f() const &{return true;} // 2
constexpr bool f() const &&{return true;} // 3 really usefull?
};
int main() {
static_assert(A{}.f()); // does not compile because call 1 instead of 3
constexpr A a;
static_assert(a.f());
}

Related

Is there a way to detect if the current member function is operating on an lvalue or rvalue?

Qualifiers are a great way to overload functions based on context of the object. Such qualifiers are const and volatile. However, writing code for them is annoying as they are usually duplication of code as the cv qualifiers are at least propagated by this. As such, we can make the entire function (sans cv qualifiers) exactly the same using propagation tricks and with the help of auto or decltype(auto).
E.g.:
struct X {
int m_i = 0;
auto& i() & { return m_i ; } // returns int &
auto& i() volatile & { return m_i ; } // returns int volatile &
auto& i() const & { return m_i ; } // returns int const &
auto& i() const volatile & { return m_i ; } // returns int const volatile &
};
Although annoying, at least we can see that this is going to work in all contexts with the same code and thus minimizes error.
However, with the new lvalue/rvalue qualifiers that can be used to specify member functions, this doesn't propagate whether the function was called on an object that is an lvalue or rvalue. This results in having to make slight variations of the function, if say I wanted to do this:
struct X {
int m_i = 0;
auto&& i() & { return m_i ; } // returns int &
auto&& i() volatile & { return m_i ; } // returns int volatile &
auto&& i() const & { return m_i ; } // returns int const &
auto&& i() const volatile & { return m_i ; } // returns int const volatile &
auto&& i() && { return std::move(m_i); } // returns int &&
auto&& i() volatile && { return std::move(m_i); } // returns int volatile &&
auto&& i() const && { return std::move(m_i); } // returns int const &&
auto&& i() const volatile && { return std::move(m_i); } // returns int const volatile &&
};
It would be nice to be able to make them all the same.
Something like this:
struct X {
int m_i = 0;
decltype(auto) i() & { return std::forward<...some_magic...>(m_i); } // returns int &
decltype(auto) i() volatile & { return std::forward<...some_magic...>(m_i); } // returns int volatile &
decltype(auto) i() const & { return std::forward<...some_magic...>(m_i); } // returns int const &
decltype(auto) i() const volatile & { return std::forward<...some_magic...>(m_i); } // returns int const volatile &
decltype(auto) i() && { return std::forward<...some_magic...>(m_i); } // returns int &&
decltype(auto) i() volatile && { return std::forward<...some_magic...>(m_i); } // returns int volatile &&
decltype(auto) i() const && { return std::forward<...some_magic...>(m_i); } // returns int const &&
decltype(auto) i() const volatile && { return std::forward<...some_magic...>(m_i); } // returns int const volatile &&
};
Is there any means to interrogate if the current member function is operating on an lvalue or rvalue?
No, there isn't.
The only access you have to yourself within the confines of a member function body is: this. And this is always a X [cv]* const, and *this is always an lvalue - an X [cv]&. It can tell you what the cv-qualifiers of the member function are, but it cannot tell you what the ref-qualifiers of the function are.
You would have to indirect all of your member functions to a non-member function, passing yourself approrpiately as an lvalue or rvalue:
decltype(auto) i() & { return free_i(*this); }
decltype(auto) i() && { return free_i(std::move(*this)); }
template <typename Self>
friend decltype(auto) free_i(Self&& self) { ... }
P0847 solves this as well, but that won't be in C++20 either. Maybe C++23:
template <typename Self>
decltype(auto) i(this Self&& self) { ... }
Here, you can straightforwardly pull off what kind of reference self is.

Is there any application for a constexpr operator++(int) (post-increment)

The following code shows how a constexpr operator++() (pre-increment) can be used (live demo). Now I wonder if there is any usefulness in declaring a post-increment operator as constexpr. I assume that such an operator has no side-effects and is used in a way similar as in the example. (If this is too abstract: I intend to extend my bitset2 class and want to know if there would be any benefit if the post-increment operator was constexpr.)
struct S {
constexpr S( int i ) : m_i( i ) {}
constexpr S & operator++() {
++m_i;
return *this;
}
int m_i;
};
int main() {
constexpr auto s1= ++S{3};
std::cout << s1.m_i << '\n'; // output: 4
}
Yes it has an application: Having a post-increment operator defined could be useful for writing constexpr functions. Consider the following:
tempalte<std::size_t N>
constexpr std::array<baz,N> foo(){
std::array<baz,N> bar;
baz i;
for(auto&& item: bar) {
item = i++;
}
return bar;
}
The array bar in the example above will be filled at compile time via the constexpr. Say the type baz was some form of counter the order of operations (pre/post) for the increment could be important.
The thing to keep in mind is that a constexpr is an expression not just a value. There is allowed to be complex (within the rules for constexpr of course) calculations that go into a constexpr. In theory you could write many programs entirely as constexpr (though I am not saying it would be a good idea).
I don't know if it's usefull, but you can duplicate your pre-increment example in a post-increment one
A compilable example
#include <iostream>
struct S
{
constexpr S (int i) : m_i{ i }
{ }
constexpr S & operator++ ()
{
++m_i;
return *this;
}
constexpr S operator++ (int)
{
S ret { this->m_i ++ };
return ret;
}
int m_i;
};
int main()
{
constexpr S s1 { ++S{3} };
constexpr S s2 { S{4}++ };
static_assert( s1.m_i == s2.m_i, "!");
}
And with both constexpr operators, you can write constexpr functions as
constexpr S foo (int a, int b)
{
S s1 { a };
S s2 { b };
bool test { (s1++).m_i == (++s2).m_i };
return test ? s1 : s2;
}

C++ Force const-ness of lvalue in initializer expression

I would like the compiler to enforce const-ness of an lvalue (non-reference) but don't know if this is possible in C++. An example:
int foo() { return 5; }
int main() {
// Is there anything I can add to the declaration of foo()
// that would make the following cause a compile-error?
int a = foo();
// Whereas this compiles fine.
const int a = foo();
}
This is not really possible with something like an int because you need to give access to read the int and if they can read the int then they can copy it into a non-const int.
But from your comments it sounds like what you have in reality is not an int but a more complex user defined type, some sort of container perhaps. You can easily create an immutable container. This container could be a wrapper, or alternative implementation of your existing container. It then doesn't matter if the caller uses a const or non-const variable it is still immutable.
class MyClass {
std::vector<int> data;
public:
MyClass(size_t size) : data(size) {}
int& operator[](size_t index) { return data[index]; }
int operator[](size_t index) const { return data[index]; }
size_t size() const { return data.size(); }
};
class MyClassImmutable {
MyClass mc;
public:
MyClassImmutable(MyClass&& mc) : mc(std::move(mc)){}
int operator[](size_t index) const { return mc[index]; }
size_t size() const { return mc.size(); }
const MyClass& get() const { return mc; }
};
MyClassImmutable foo() {
MyClass mc(100);
mc[10] = 3;
return mc;
}
void func(const MyClass& mc);
int main() {
MyClassImmutable mci = foo();
std::cout << mci[10] << "\n"; // Can read individual values
//mci[10] = 4; // Error immutable
func(mc.get()); // call function taking a const MyClass&
}
Live demo.
Of course there is nothing to stop the caller from copying each and every value from your immutable container and inserting them into a mutable container.
Edit: An alternative approach might be to return a smart pointer-to-const. The only downside is you have to pay for a dynamic memory allocation:
std::unique_ptr<const MyClass> foo() {
auto mc = std::make_unique<MyClass>(100);
(*mc)[10] = 3;
return mc;
}
void func(const MyClass& mc);
int main() {
auto mc = foo();
std::cout << (*mc)[10] << "\n"; // Can read individual values
//(*mc)[10] = 4; // Error const
func(*mc); // can pass to a function taking a const MyClass&
}
It's not possible. foo() has no way of knowing about the type of the left hand side of the assignment, because when the assignment itself happens, foo() is already evaluated. The best you could hope for is to change the return value, to try and cause a type-based error on the initialization:
#include <type_traits>
struct my_int {
const int m;
template<typename T, typename std::enable_if<std::is_const<T>::value, T>::type* = nullptr>
constexpr operator T() const {return m;}
};
constexpr my_int foo() { return {5};}
int main() {
const int a = foo();
int b = foo();
}
Live example
But this will also not work, because the typename in the template will never be substitued by a const-qualified type (in this specific case, it will be int for both lines in main()).
As the following is possible
const int x = 4;
int y = x;
the C++ language will not provide such a mechanism.
Remains making a int const by a macro mechanism.
#define int_const_foo(var) const int var = ___foo()
int_const_foo(a);
Drawback: foo cannot be hidden, and the syntax is no longer C style.

static constexpr function different than global?

I cannot understand why static constexpr behaves differently then global constexpr. What am I doing wrong? Compiler error is not particularly helpful:
prog.cpp:20:17: error: ‘static constexpr int Bar::foo(const char*)’ called in a constant expression
B = Bar::foo(s)
^
prog.cpp:20:17: error: enumerator value for ‘B’ is not an integer constant
code is as follows:
#include <iostream>
constexpr int foo(const char* v)
{
return v[0];
}
struct Bar
{
static constexpr int foo(const char* v)
{
return v[0];
}
static constexpr const char* s = "abc";
enum
{
A = ::foo(s),
B = Bar::foo(s)
};
};
int main()
{
int a[Bar::A];
a[0] = Bar::A;
}
This is because the enum is part of the class declaration, whereas the definitions of the functions inside the class are logically deferred until after the class declaration. This means that as far as the enum is concerned, it can't see the definition of B::foo, and therefore it can't use a call to it as a constant expression. You can see this by putting the enum after the class:
#include <iostream>
constexpr int foo(const char* v)
{
return v[0];
}
struct Bar
{
static constexpr int foo(const char* v)
{
return v[0];
}
static constexpr const char* s = "abc";
};
enum Enum
{
A = ::foo(Bar::s),
B = Bar::foo(Bar::s)
};
which gives no error.

Using non-const variable inside constexpr?

Consequently to a previous question about const/non-const with ternary operator, is the following test function ok regarding to the C++11 standard :
template<bool UseConst> class MyClass
{
public:
constexpr bool test()
{
return (UseConst) ? (_constvar) : (_var);
}
protected:
int _var;
static const int _constvar;
}
The whole problem, is that _constvar is const, and _var is non-const. I would have to access these 2 data depending on the template parameter through the same function, and I would like to have a compile-time function when I use const.
Do the test() function satisfy my requirements ?
You could use SFINAE in order to "specialize" your test function. In other words, you could do something like the following:
template<bool true_false>
struct true_type
{
static char value;
};
template<>
struct true_type<false>
{
static char value[2];
};
template<bool UseConst> class MyClass
{
private:
constexpr int pre_test(const char arg) { return _constvar; }
int pre_test(char (&)[2]) const { return _var; }
public:
constexpr int test()
{
return pre_test(true_type<UseConst>::value);
}
protected:
int _var;
static const int _constvar;
};
Now, when you call MyClass::test, if UseConst is false, test will degrade to a run-time function, but when UseConst is true, you will get a compile-time function.