Preferred way of conditionally initializing a const - c++11

What is the preferred way to initialize a const automatic variable? I can think of several.
if statement:
const std::string s;
if( condition ) { const_cast<std::string&>(s) = "first"; }
else { const_cast<std::string&>(s) = "second"; }
?: operator:
const std::string s = ( condition ) ? "first" : second;
immediately invoked function expression:
const std::string s = [ & ] ()
{ if( condition ) return "first" else return "second" } ();

C++ Core Guidelines advise to use lambdas for complex initialization, especially of const variables.

Your if statement example does not initialize any const, as s is not const.
Regarding other options (this is mainly subjective), here are my guidelines:
Use the ?: (ternary operator) when the expression is short and easy to read. In your case, I think it would be fine.
Use a IIFE (immediately invoked function expression) or refactor the initialization to a function which returns a value when the initialization logic is long and complex and when it would negatively impact readability of the function where the variable is initialized.

What is the preferred way to initialize a const automatic variable?
The preferred way is as simple as possible.
With a conditional statement:
std::string const a = condition ? "second" : "first";
With a function call (C++17 constexpr here):
constexpr char const* get_const(int condition) {
switch(condition) {
case 0: return "first";
case 1: return "second";
default: return "default";
}
}
std::string const b = get_const(condition);

Related

Using find_if with a vector of pointers: How to pass pointer by const reference to lambda?

In the following code I try to compare a vector of pointers via find_if and determine which contains a member a == 5 (in this case both of course, but it shows my case). However it doesn't compile.
#include <algorithm>
class obj
{
public:
int a = 5;
int b = 2;
};
int main()
{
obj A;
obj B;
std::vector<obj*> v = { &A, &B };
std::find_if(begin(v), end(v), [](const (obj*)& instance) { if((*instance)->a == 5) return true; });
}
From what I interpreted here, find_if provides the actual vector entry as parameter to the lambda function which is traditionally taken up via const ref. But how do I specify this for pointers, because I have pointers as vector entries?
(For the lengthy error message take this code to godbolt using gcc 11.1 but I guess it's down to me not knowing how to specify the lambda argument correctly)
You want to have const reference to pointer, not reference to const pointer:
[](obj* const& instance) { if(instance->a == 5) return true; return false; }
or with type alias for obj pointer, it is much clearer:
using PtrObj = obj*;
std::find_if(begin(v), end(v), [](const PtrObj& instance) { if(instance->a == 5) return true; return false; });

C++ STL; Iterate over class which contains STL container?

I have classes for linear algebra, specifically vectors and matrices. These contain among others std::vectors (or std::maps) as their 'data' fields.
Iterating over these in a range based for loop is easy. But I'd like to make these fields private and make iterating over my custom classes more natural, such that I can do range based for loops over the class itself.
I tried looking at the function definition of std::vector<>.begin() and such. Then I tried to implement it in such a way that all the iterator objects are forwaded from the std::vector<> fields, but to no avail. When I try to iterate over a constant instance of my class, with the following example;
int main() {
AlgebraLib::Vector A(4, true);
A[0] = 4;
A[1] = 5;
A[2] = 6;
A[3] = 7;
for (auto &&item : A) {
std::cout << item << std::endl;
}
const AlgebraLib::Vector B = A;
for (auto &&item : B) {
std::cout << item << std::endl;
}
return EXIT_SUCCESS;
}
... I get the following compiler error;
error: passing ‘const AlgebraLib::Vector’ as ‘this’ argument discards qualifiers [-fpermissive]
for (auto &&item : B) {
Basically, all iterators are defined like this:
std::vector<double>::iterator Vector::begin() {
return _VectorContents.begin();
}
std::vector<double>::iterator Vector::end() {
return _VectorContents.end();
}
std::vector<double>::reverse_iterator Vector::rbegin() {
return _VectorContents.rbegin();
}
std::vector<double>::reverse_iterator Vector::rend() {
return _VectorContents.rend();
}
std::vector<double>::const_iterator Vector::cbegin() const noexcept{
return _VectorContents.cbegin();
}
std::vector<double>::const_iterator Vector::cend() const noexcept{
return _VectorContents.cend();
}
std::vector<double>::const_reverse_iterator Vector::crbegin() const noexcept{
return _VectorContents.crbegin();
}
std::vector<double>::const_reverse_iterator Vector::crend() const noexcept{
return _VectorContents.crend();
}
And in my header Vector.hpp:
// Iterators
std::vector<double>::iterator begin();
std::vector<double>::iterator end();
std::vector<double>::reverse_iterator rbegin();
std::vector<double>::reverse_iterator rend();
std::vector<double>::const_iterator cbegin() const noexcept;
std::vector<double>::const_iterator cend() const noexcept;
std::vector<double>::const_reverse_iterator crbegin() const noexcept;
std::vector<double>::const_reverse_iterator crend() const noexcept;
I feel like I'm missing something here? I'm relatively new to C++, and I work at the moment with C++11.
The problem is that B is const, but the range-for loop only uses begin and end (not cbegin and cend) and your begin and end functions have no overloads that are const. This is a problem because only functions that are marked as const can be called on const objects.
The solution is simple: Just add such overloads
std::vector<double>::const_iterator begin() const;
std::vector<double>::const_iterator end() const;
The implementation of these overloads will be just the same as for the non-const functions.

std::remove vs std::remove_if

I have a following class:
class Foo
{
public:
void Fill();
private:
std::vector<std::wstring> vec;
};
And then the implementation is:
void Foo::Fill()
{
vec.push_back( L"abc aaa" );
vec.push_back( L"def aaa" );
vec.push_back( L"fed bbb" );
vec.push_back( L"cba bbb" );
}
What I'd like to do to delete an element from this vector, lets say one that contains "def". What would be the easiest way to do so?
I'm thinking to use remove_if, but the comparator accept only 1 parameter - container element.
Is there an elegant solution? I'd use looping as a last resort.
If it's a class rather than a function, the predicate has a constructor, and constructors can take arguments.
You can pass your search string into this constructor, store it as a member, then use it inside the function call operator:
struct Pred
{
Pred(std::wstring str) : str(std::move(str)) {}
bool operator()(const std::wstring& el) const
{
return el.find(str) != el.npos;
}
private:
const std::wstring str;
};
std::remove_if(std::begin(vec), std::end(vec), Pred("def"));
// N.B. `el.find` is probably wrong. Whatever.
A "shortcut" to doing this is to use a lambda for the predicate, then the passing through of the search string is done for you either via the lambda capture, or by virtue of the fact that you literally typed it in right there and then:
std::remove_if(
std::begin(vec), std::end(vec),
[](const auto& el) { return el.find("def") != el.npos; }
);
// N.B. `el.find` is probably still wrong. Whatever.
If the predicate is a function, and you want it to invoke a binary comparator, you could mess about with std::bind to achieve the same result.

How to return nullptr from a lambda function?

I have a small lambda function which shall find and return a QTreeWidgetItem. But if it does not find the given item, then it shall return a nullptr. But if I try to compile it then it gives me an error.
The function:
auto takeTopLevelItem = []( QTreeWidget* aTreeWidget, const QString& aText )
{
const int count = aTreeWidget->topLevelItemCount();
for ( int index = 0; index < count; ++index )
{
auto item = aTreeWidget->topLevelItem( index );
if ( item->text( 0 ) == aText )
{
return aTreeWidget->takeTopLevelItem( index );
}
}
return nullptr; // This causes a compilation error.
};
The error:
Error 1 error C3487: 'nullptr': all return expressions in a lambda must have the same type: previously it was 'QTreeWidgetItem *' cpp 251
I changed the mentioned line with this and now it compiles:
return (QTreeWidgetItem*)( nullptr );
but I would like to avoid this syntax. How can I solve this ?
I use Visual Studio 2012.
You can add an explicit return type annotation:
auto takeTopLevelItem = []( ... ) -> QTreeWidgetItem*
{
// ...
}
That way nullptr will be converted to your pointer type properly. You're getting that error because the lambda assumes no conversions should be made, and treats nullptr_t as a legitimate alternative return type.
As a side note, consider using (std::)optional instead. The nullability of pointers can be used to represent a missing return, but it doesn't mean it necessarily should be.
If you just want to avoid the syntax, rather than the casting, you could it like this:
static_cast<QTreeWidgetItem*>(nullptr);
I made a small example, on how Bartek's and mine's answer really work:
#include <iostream>
class A {
int a;
};
auto bla = [] (A* obj, bool flag) -> A* {
if(flag)
return obj;
return nullptr;
// return static_cast<A*>(nullptr);
};
int main() {
A obj;
A* ptr = &obj;
bool flag = false;
if( bla(ptr, flag) == nullptr)
std::cout << "ok\n";
return 0;
}
I had this very same issue with some Smart Pointers, so I found I could do this to avoid the issue:
auto myFunc= [](MyClass* class)
{
MyPointer* pointer = nullptr;
if( class && class->isValid() )
{
pointer = class->getPointerInstance()
}
return pointer;
}
Similarly, for shared pointer, just repleace MyPointer* by std::shared_ptr<MyPointer>.
So your code would looks like:
auto takeTopLevelItem = []( QTreeWidget* aTreeWidget, const QString& aText )
{
QTreeWidgetItem* item = nullptr;
const int count = aTreeWidget->topLevelItemCount();
for ( int index = 0; index < count; ++index )
{
auto item = aTreeWidget->topLevelItem( index );
if ( item->text( 0 ) == aText )
{
item = aTreeWidget->takeTopLevelItem( index );
break;
}
}
return item;
};

How to use ranged-based for loops with std::weak_ptr

Can you use a C++11 ranged-based for loop with an std::weak_ptr instead of using a for_each with lambda, as seen below?
std::vector<std::weak_ptr<IObservable>> observers;
std::for_each(this->observers.cbegin(), this->observers.cend(),
[](const std::weak_ptr<IObservable>& _o)
{
auto observer = _o.lock();
if (observer)
{
observer->Notify()
}
});
I don't see why not. Are you getting any error messages?
this:
for( const auto& o: observers ) {
if( auto observer = o.lock() ) {
observer->Notify();
}
}
should work ok... :D
Considering that std::for_each most likely translates to:
template<class InputIt, class UnaryFunction>
UnaryFunction for_each(InputIt first, InputIt last, UnaryFunction f)
{
for (; first != last; ++first) {
f(*first);
}
return f;
}
and that range-based for loops translates to:
{
auto && __range = range_expression ;
for (auto __begin = begin_expr,
__end = end_expr;
__begin != __end; ++__begin) {
range_declaration = *__begin;
loop_statement
}
}
You can see just few differences:
the latter does not allow iterators different from begin() and end()
the former returns the unary function
But in the overall picture, using it in place of your std::for_each, in you situation, would work perfectly fine.
And here's how:
std::vector<std::weak_ptr<IObservable>> observers;
for (const auto& _o) {
auto observer = _o.lock();
if (observer)
observer->Notify();
}

Resources